Merge "Fix ConnectedBluetoothDevice strings for TV." into main
diff --git a/DREAM_MANAGER_OWNERS b/DREAM_MANAGER_OWNERS
new file mode 100644
index 0000000..48bde60
--- /dev/null
+++ b/DREAM_MANAGER_OWNERS
@@ -0,0 +1 @@
+brycelee@google.com
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index e7adf20..f6213b9 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -31,6 +31,7 @@
         "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
 
     srcs: [
+        ":aconfigd_protos",
         ":ipconnectivity-proto-src",
         ":libstats_atom_enum_protos",
         ":libstats_atom_message_protos",
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 74b34fb..412f2b7 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -104,6 +104,18 @@
     ],
 }
 
+genrule {
+    name: "framework-minus-apex.ravenwood.keep_all",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
+    cmd: "cp $(in) $(out)",
+    srcs: [
+        ":framework-minus-apex.ravenwood-base{hoststubgen_keep_all.txt}",
+    ],
+    out: [
+        "hoststubgen_framework-minus-apex_keep_all.txt",
+    ],
+}
+
 java_library {
     name: "services.core-for-hoststubgen",
     installable: false, // host only jar.
@@ -189,6 +201,18 @@
     ],
 }
 
+genrule {
+    name: "services.core.ravenwood.keep_all",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
+    cmd: "cp $(in) $(out)",
+    srcs: [
+        ":services.core.ravenwood-base{hoststubgen_keep_all.txt}",
+    ],
+    out: [
+        "hoststubgen_services.core_keep_all.txt",
+    ],
+}
+
 java_library {
     name: "services.core.ravenwood-jarjar",
     installable: false,
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 384d786..ff73a49 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -80,6 +80,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.os.storage.StorageManagerInternal;
@@ -179,6 +180,8 @@
     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     public static final boolean DEBUG_STANDBY = DEBUG || false;
 
+    public static final String TRACE_TRACK_NAME = "JobScheduler";
+
     /** The maximum number of jobs that we allow an app to schedule */
     private static final int MAX_JOBS_PER_APP = 150;
     /** The number of the most recently completed jobs to keep track of for debugging purposes. */
@@ -4344,7 +4347,11 @@
 
                 final boolean wasConsideredCharging = isConsideredCharging();
                 mChargingPolicy = newPolicy;
-
+                if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+                    Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+                            JobSchedulerService.TRACE_TRACK_NAME,
+                            "CHARGING POLICY CHANGED#" + mChargingPolicy);
+                }
                 if (isConsideredCharging() != wasConsideredCharging) {
                     for (int c = mControllers.size() - 1; c >= 0; --c) {
                         mControllers.get(c).onBatteryStateChangedLocked();
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 39d50f5..d65a66c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -535,29 +535,17 @@
             sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
             final String sourcePackage = job.getSourcePackageName();
             if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
-                final String componentPackage = job.getServiceComponent().getPackageName();
-                String traceTag = "*job*<" + job.getSourceUid() + ">" + sourcePackage;
-                if (!sourcePackage.equals(componentPackage)) {
-                    traceTag += ":" + componentPackage;
-                }
-                traceTag += "/" + job.getServiceComponent().getShortClassName();
-                if (!componentPackage.equals(job.serviceProcessName)) {
-                    traceTag += "$" + job.serviceProcessName;
-                }
-                if (job.getNamespace() != null) {
-                    traceTag += "@" + job.getNamespace();
-                }
-                traceTag += "#" + job.getJobId();
-
                 // Use the context's ID to distinguish traces since there'll only be one job
                 // running per context.
-                Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
-                        traceTag, getId());
+                Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
+                        JobSchedulerService.TRACE_TRACK_NAME, job.computeSystemTraceTag(),
+                        getId());
             }
             if (job.getAppTraceTag() != null) {
                 // Use the job's ID to distinguish traces since the ID will be unique per app.
-                Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "JobScheduler",
-                        job.getAppTraceTag(), job.getJobId());
+                Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP,
+                        JobSchedulerService.TRACE_TRACK_NAME, job.getAppTraceTag(),
+                        job.getJobId());
             }
             try {
                 mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
@@ -1605,12 +1593,12 @@
                 completedJob.getFilteredTraceTag(),
                 completedJob.getFilteredDebugTags());
         if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
-            Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
-                    getId());
+            Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER,
+                    JobSchedulerService.TRACE_TRACK_NAME, getId());
         }
         if (completedJob.getAppTraceTag() != null) {
-            Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "JobScheduler",
-                    completedJob.getJobId());
+            Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP,
+                    JobSchedulerService.TRACE_TRACK_NAME, completedJob.getJobId());
         }
         try {
             mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
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 7fca867..e3af1d8 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
@@ -572,6 +572,9 @@
     /** The reason a job most recently went from ready to not ready. */
     private int mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
 
+    /** The system trace tag for this job. */
+    private String mSystemTraceTag;
+
     /**
      * Core constructor for JobStatus instances.  All other ctors funnel down to this one.
      *
@@ -1058,6 +1061,38 @@
         return job.getTraceTag();
     }
 
+    /** Returns a trace tag using debug information provided by job scheduler service. */
+    @NonNull
+    public String computeSystemTraceTag() {
+        // Guarded by JobSchedulerService.mLock, no need for synchronization.
+        if (mSystemTraceTag != null) {
+            return mSystemTraceTag;
+        }
+
+        mSystemTraceTag = computeSystemTraceTagInner();
+        return mSystemTraceTag;
+    }
+
+    @NonNull
+    private String computeSystemTraceTagInner() {
+        final String componentPackage = getServiceComponent().getPackageName();
+        StringBuilder traceTag = new StringBuilder(128);
+        traceTag.append("*job*<").append(sourceUid).append(">").append(sourcePackageName);
+        if (!sourcePackageName.equals(componentPackage)) {
+            traceTag.append(":").append(componentPackage);
+        }
+        traceTag.append("/").append(getServiceComponent().getShortClassName());
+        if (!componentPackage.equals(serviceProcessName)) {
+            traceTag.append("$").append(serviceProcessName);
+        }
+        if (mNamespace != null && !mNamespace.trim().isEmpty()) {
+            traceTag.append("@").append(mNamespace);
+        }
+        traceTag.append("#").append(getJobId());
+
+        return traceTag.toString();
+    }
+
     /** Returns whether this job was scheduled by one app on behalf of another. */
     public boolean isProxyJob() {
         return mIsProxyJob;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index c240b3f..a1c72fb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -50,6 +50,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.util.ArraySet;
@@ -2181,6 +2182,12 @@
                     }
                     scheduleCutoff();
                 }
+            } else {
+                if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+                    Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+                            JobSchedulerService.TRACE_TRACK_NAME,
+                            "QC/- " + mPkg);
+                }
             }
         }
 
@@ -2720,6 +2727,11 @@
                         if (timeRemainingMs <= 50) {
                             // Less than 50 milliseconds left. Start process of shutting down jobs.
                             if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
+                            if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+                                Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+                                        JobSchedulerService.TRACE_TRACK_NAME,
+                                        pkg + "#" + MSG_REACHED_TIME_QUOTA);
+                            }
                             mStateChangedListener.onControllerStateChanged(
                                     maybeUpdateConstraintForPkgLocked(
                                             sElapsedRealtimeClock.millis(),
@@ -2748,6 +2760,11 @@
                                 pkg.userId, pkg.packageName);
                         if (timeRemainingMs <= 0) {
                             if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
+                            if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+                                Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+                                        JobSchedulerService.TRACE_TRACK_NAME,
+                                        pkg + "#" + MSG_REACHED_EJ_TIME_QUOTA);
+                            }
                             mStateChangedListener.onControllerStateChanged(
                                     maybeUpdateConstraintForPkgLocked(
                                             sElapsedRealtimeClock.millis(),
@@ -2772,6 +2789,12 @@
                             Slog.d(TAG, pkg + " has reached its count quota.");
                         }
 
+                        if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+                            Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+                                    JobSchedulerService.TRACE_TRACK_NAME,
+                                    pkg + "#" + MSG_REACHED_COUNT_QUOTA);
+                        }
+
                         mStateChangedListener.onControllerStateChanged(
                                 maybeUpdateConstraintForPkgLocked(
                                         sElapsedRealtimeClock.millis(),
@@ -2928,6 +2951,11 @@
                             }
                             mTempAllowlistGraceCache.delete(uid);
                             mTopAppGraceCache.delete(uid);
+                            if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+                                Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+                                        JobSchedulerService.TRACE_TRACK_NAME,
+                                        "<" + uid + ">#" + MSG_END_GRACE_PERIOD);
+                            }
                             final ArraySet<String> packages = mService.getPackagesForUidLocked(uid);
                             if (packages != null) {
                                 final int userId = UserHandle.getUserId(uid);
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 9b81bd4..13d6ae5 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -50,7 +50,7 @@
         },
         api_lint: {
             enabled: true,
-            new_since: ":android.api.public.latest",
+            new_since: ":android.api.combined.public.latest",
             baseline_file: ":non-updatable-lint-baseline.txt",
         },
     },
@@ -130,7 +130,7 @@
         },
         api_lint: {
             enabled: true,
-            new_since: ":android.api.system.latest",
+            new_since: ":android.api.combined.system.latest",
             baseline_file: ":non-updatable-system-lint-baseline.txt",
         },
     },
@@ -185,7 +185,7 @@
         },
         api_lint: {
             enabled: true,
-            new_since: ":android.api.test.latest",
+            new_since: ":android.api.combined.test.latest",
             baseline_file: ":non-updatable-test-lint-baseline.txt",
         },
     },
@@ -269,7 +269,7 @@
         },
         api_lint: {
             enabled: true,
-            new_since: ":android.api.module-lib.latest",
+            new_since: ":android.api.combined.module-lib.latest",
             baseline_file: ":non-updatable-module-lib-lint-baseline.txt",
         },
     },
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 77b74e9..5adcd93 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -707,11 +707,11 @@
     eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     eglDestroySurface(mDisplay, mSurface);
 
-    mFlingerSurfaceControl->updateDefaultBufferSize(newWidth, newHeight);
     const auto limitedSize = limitSurfaceSize(newWidth, newHeight);
     mWidth = limitedSize.width;
     mHeight = limitedSize.height;
 
+    mFlingerSurfaceControl->updateDefaultBufferSize(mWidth, mHeight);
     EGLConfig config = getEglConfig(mDisplay);
     EGLSurface surface = eglCreateWindowSurface(mDisplay, config, mFlingerSurface.get(), nullptr);
     if (eglMakeCurrent(mDisplay, surface, surface, mContext) == EGL_FALSE) {
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
index 488292d..f726361 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
@@ -292,13 +292,17 @@
         int childCount = node.getChildCount();
         for (int x = 0; x < childCount; x++) {
             AccessibilityNodeInfo childNode = node.getChild(x);
-
+            if (childNode == null) {
+                continue;
+            }
             if (!safeCharSeqToString(childNode.getContentDescription()).isEmpty()
-                    || !safeCharSeqToString(childNode.getText()).isEmpty())
+                    || !safeCharSeqToString(childNode.getText()).isEmpty()) {
                 return true;
+            }
 
-            if (childNafCheck(childNode))
+            if (childNafCheck(childNode)) {
                 return true;
+            }
         }
         return false;
     }
diff --git a/core/api/current.txt b/core/api/current.txt
index 53cf7d5..13958d2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -26592,7 +26592,7 @@
     method public long getFlags();
     method @Nullable public android.media.MediaMetadata getMetadata();
     method public String getPackageName();
-    method @Nullable public android.media.session.MediaController.PlaybackInfo getPlaybackInfo();
+    method @NonNull public android.media.session.MediaController.PlaybackInfo getPlaybackInfo();
     method @Nullable public android.media.session.PlaybackState getPlaybackState();
     method @Nullable public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
     method @Nullable public CharSequence getQueueTitle();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ba1bcc8..96315eb 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1143,16 +1143,13 @@
     field public static final int NAV_BAR_MODE_KIDS = 1; // 0x1
   }
 
-  public static final class StatusBarManager.DisableInfo implements android.os.Parcelable {
+  public static final class StatusBarManager.DisableInfo {
     method public boolean areAllComponentsEnabled();
-    method public int describeContents();
     method public boolean isNavigateToHomeDisabled();
     method public boolean isNotificationPeekingDisabled();
     method public boolean isRecentsDisabled();
     method public boolean isSearchDisabled();
     method public boolean isStatusBarExpansionDisabled();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.StatusBarManager.DisableInfo> CREATOR;
   }
 
   public final class SystemServiceRegistry {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 62fc67b..78577e2 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -1939,10 +1939,6 @@
     New API must be flagged with @FlaggedApi: method android.app.ActivityManager.getExternalHistoricalProcessStartReasons(String,int)
 UnflaggedApi: android.app.AppOpsManager#OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO:
     New API must be flagged with @FlaggedApi: field android.app.AppOpsManager.OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO
-UnflaggedApi: android.app.StatusBarManager.DisableInfo#CREATOR:
-    New API must be flagged with @FlaggedApi: field android.app.StatusBarManager.DisableInfo.CREATOR
-UnflaggedApi: android.app.StatusBarManager.DisableInfo#isBackDisabled():
-    New API must be flagged with @FlaggedApi: method android.app.StatusBarManager.DisableInfo.isBackDisabled()
 UnflaggedApi: android.companion.virtual.VirtualDeviceManager.VirtualDevice#getPersistentDeviceId():
     New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceManager.VirtualDevice.getPersistentDeviceId()
 UnflaggedApi: android.content.Context#THREAD_NETWORK_SERVICE:
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 211fc96..14ae3f5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -463,7 +463,7 @@
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void togglePanel();
   }
 
-  public static final class StatusBarManager.DisableInfo implements android.os.Parcelable {
+  public static final class StatusBarManager.DisableInfo {
     method public boolean isRotationSuggestionDisabled();
   }
 
@@ -4260,6 +4260,12 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentOrganizerToken> CREATOR;
   }
 
+  public final class TaskFragmentParentInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentParentInfo> CREATOR;
+  }
+
   public final class TaskFragmentTransaction implements android.os.Parcelable {
     ctor public TaskFragmentTransaction();
     method public void addChange(@Nullable android.window.TaskFragmentTransaction.Change);
@@ -4284,8 +4290,8 @@
     method @Nullable public android.os.IBinder getActivityToken();
     method @NonNull public android.os.Bundle getErrorBundle();
     method @Nullable public android.os.IBinder getErrorCallbackToken();
-    method @Nullable public android.content.res.Configuration getTaskConfiguration();
     method @Nullable public android.window.TaskFragmentInfo getTaskFragmentInfo();
+    method @Nullable public android.window.TaskFragmentParentInfo getTaskFragmentParentInfo();
     method @Nullable public android.os.IBinder getTaskFragmentToken();
     method public int getTaskId();
     method public int getType();
@@ -4293,7 +4299,6 @@
     method @NonNull public android.window.TaskFragmentTransaction.Change setActivityToken(@NonNull android.os.IBinder);
     method @NonNull public android.window.TaskFragmentTransaction.Change setErrorBundle(@NonNull android.os.Bundle);
     method @NonNull public android.window.TaskFragmentTransaction.Change setErrorCallbackToken(@Nullable android.os.IBinder);
-    method @NonNull public android.window.TaskFragmentTransaction.Change setTaskConfiguration(@NonNull android.content.res.Configuration);
     method @NonNull public android.window.TaskFragmentTransaction.Change setTaskFragmentInfo(@NonNull android.window.TaskFragmentInfo);
     method @NonNull public android.window.TaskFragmentTransaction.Change setTaskFragmentToken(@NonNull android.os.IBinder);
     method @NonNull public android.window.TaskFragmentTransaction.Change setTaskId(int);
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 7ee3413..497d47a 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -2015,7 +2015,7 @@
      *     null for no callback
      * @param handler {@link Handler} identifying the callback thread,
      *     null for the main thread
-     * @return An {@link AccountManagerFuture} which resolves to a Boolean indicated wether it
+     * @return An {@link AccountManagerFuture} which resolves to a Boolean indicated whether it
      * succeeded.
      * @hide
      */
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a5dd4a7..e1cb630 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5796,6 +5796,8 @@
      * @see #onRequestPermissionsResult
      */
     @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+    @SuppressLint("OnNameExpected")
+    // Suppress lint as this is an overload of the original API.
     public boolean shouldShowRequestPermissionRationale(@NonNull String permission, int deviceId) {
         final PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager()
                 : createDeviceContext(deviceId).getPackageManager();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 9ea55f5..c6a1546 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -103,7 +103,8 @@
     @IntDef(prefix = {"MODE_BACKGROUND_ACTIVITY_START_"}, value = {
             MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED,
             MODE_BACKGROUND_ACTIVITY_START_ALLOWED,
-            MODE_BACKGROUND_ACTIVITY_START_DENIED})
+            MODE_BACKGROUND_ACTIVITY_START_DENIED,
+            MODE_BACKGROUND_ACTIVITY_START_COMPAT})
     public @interface BackgroundActivityStartMode {}
     /**
      * No explicit value chosen. The system will decide whether to grant privileges.
@@ -117,6 +118,13 @@
      * Deny the {@link PendingIntent} to use the background activity start privileges.
      */
     public static final int MODE_BACKGROUND_ACTIVITY_START_DENIED = 2;
+    /**
+     * Special behavior for compatibility.
+     * Similar to {@link #MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED}
+     *
+     * @hide
+     */
+    public static final int MODE_BACKGROUND_ACTIVITY_START_COMPAT = -1;
 
     /**
      * The package name that created the options.
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 7724c23..92543b1 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -32,6 +32,11 @@
     public boolean topActivityEligibleForLetterboxEducation;
 
     /**
+     * Whether the letterbox education is enabled
+     */
+    public boolean isLetterboxEducationEnabled;
+
+    /**
      * Whether the direct top activity is in size compat mode on foreground.
      */
     public boolean topActivityInSizeCompat;
@@ -178,6 +183,7 @@
                     == that.topActivityEligibleForUserAspectRatioButton
                 && topActivityEligibleForLetterboxEducation
                     == that.topActivityEligibleForLetterboxEducation
+                && isLetterboxEducationEnabled == that.isLetterboxEducationEnabled
                 && topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition
                 && topActivityLetterboxHorizontalPosition
                     == that.topActivityLetterboxHorizontalPosition
@@ -192,6 +198,7 @@
      * Reads the AppCompatTaskInfo from a parcel.
      */
     void readFromParcel(Parcel source) {
+        isLetterboxEducationEnabled = source.readBoolean();
         topActivityInSizeCompat = source.readBoolean();
         topActivityEligibleForLetterboxEducation = source.readBoolean();
         isLetterboxDoubleTapEnabled = source.readBoolean();
@@ -212,6 +219,7 @@
      */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(isLetterboxEducationEnabled);
         dest.writeBoolean(topActivityInSizeCompat);
         dest.writeBoolean(topActivityEligibleForLetterboxEducation);
         dest.writeBoolean(isLetterboxDoubleTapEnabled);
@@ -232,6 +240,7 @@
         return "AppCompatTaskInfo { topActivityInSizeCompat=" + topActivityInSizeCompat
                 + " topActivityEligibleForLetterboxEducation= "
                 + topActivityEligibleForLetterboxEducation
+                + "isLetterboxEducationEnabled= " + isLetterboxEducationEnabled
                 + " isLetterboxDoubleTapEnabled= " + isLetterboxDoubleTapEnabled
                 + " topActivityEligibleForUserAspectRatioButton= "
                 + topActivityEligibleForUserAspectRatioButton
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2d0f6fc..54f6909 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -482,7 +482,8 @@
             UID_STATE_FOREGROUND_SERVICE,
             UID_STATE_FOREGROUND,
             UID_STATE_BACKGROUND,
-            UID_STATE_CACHED
+            UID_STATE_CACHED,
+            UID_STATE_NONEXISTENT
     })
     public @interface UidState {}
 
@@ -566,6 +567,12 @@
     public static final int MIN_PRIORITY_UID_STATE = UID_STATE_CACHED;
 
     /**
+     * Special uid state: The UID is not running
+     * @hide
+     */
+    public static final int UID_STATE_NONEXISTENT = Integer.MAX_VALUE;
+
+    /**
      * Resolves the first unrestricted state given an app op.
      * @param op The op to resolve.
      * @return The last restricted UID state.
@@ -596,6 +603,7 @@
             UID_STATE_FOREGROUND,
             UID_STATE_BACKGROUND,
             UID_STATE_CACHED
+            // UID_STATE_NONEXISTENT isn't a real UID state, so it is excluded
     };
 
     /** @hide */
@@ -615,6 +623,8 @@
                 return "bg";
             case UID_STATE_CACHED:
                 return "cch";
+            case UID_STATE_NONEXISTENT:
+                return "gone";
             default:
                 return "unknown";
         }
diff --git a/core/java/android/app/ComponentOptions.java b/core/java/android/app/ComponentOptions.java
index 397477d..0e8e2e3 100644
--- a/core/java/android/app/ComponentOptions.java
+++ b/core/java/android/app/ComponentOptions.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityOptions.BackgroundActivityStartMode;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
 
@@ -54,7 +55,7 @@
     public static final String KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION =
             "android.pendingIntent.backgroundActivityAllowedByPermission";
 
-    private @Nullable Boolean mPendingIntentBalAllowed = null;
+    private Integer mPendingIntentBalAllowed = MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
     private boolean mPendingIntentBalAllowedByPermission = false;
 
     ComponentOptions() {
@@ -65,12 +66,9 @@
         // results they want, which is their loss.
         opts.setDefusable(true);
 
-        boolean pendingIntentBalAllowedIsSetExplicitly =
-                opts.containsKey(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED);
-        if (pendingIntentBalAllowedIsSetExplicitly) {
-            mPendingIntentBalAllowed =
-                    opts.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED);
-        }
+        mPendingIntentBalAllowed =
+                opts.getInt(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
+                        MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
         setPendingIntentBackgroundActivityLaunchAllowedByPermission(
                 opts.getBoolean(
                         KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, false));
@@ -85,7 +83,8 @@
      * @hide
      */
     @Deprecated public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) {
-        mPendingIntentBalAllowed = allowed;
+        mPendingIntentBalAllowed = allowed ? MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+                : MODE_BACKGROUND_ACTIVITY_START_DENIED;
     }
 
     /**
@@ -98,11 +97,8 @@
      * @hide
      */
     @Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
-        if (mPendingIntentBalAllowed == null) {
-            // cannot return null, so return the value used up to API level 33 for compatibility
-            return true;
-        }
-        return mPendingIntentBalAllowed;
+        // cannot return all detail, so return the value used up to API level 33 for compatibility
+        return mPendingIntentBalAllowed != MODE_BACKGROUND_ACTIVITY_START_DENIED;
     }
 
     /**
@@ -119,16 +115,15 @@
             @BackgroundActivityStartMode int state) {
         switch (state) {
             case MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
-                mPendingIntentBalAllowed = null;
-                break;
-            case MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
-                mPendingIntentBalAllowed = true;
-                break;
             case MODE_BACKGROUND_ACTIVITY_START_DENIED:
-                mPendingIntentBalAllowed = false;
+            case MODE_BACKGROUND_ACTIVITY_START_COMPAT:
+            case MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
+                mPendingIntentBalAllowed = state;
                 break;
             default:
-                throw new IllegalArgumentException(state + " is not valid");
+                // Assume that future values are some variant of allowing the start.
+                mPendingIntentBalAllowed = MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+                break;
         }
         return this;
     }
@@ -141,13 +136,7 @@
      * @see #setPendingIntentBackgroundActivityStartMode(int)
      */
     public @BackgroundActivityStartMode int getPendingIntentBackgroundActivityStartMode() {
-        if (mPendingIntentBalAllowed == null) {
-            return MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
-        } else if (mPendingIntentBalAllowed) {
-            return MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
-        } else {
-            return MODE_BACKGROUND_ACTIVITY_START_DENIED;
-        }
+        return mPendingIntentBalAllowed;
     }
 
     /**
@@ -170,8 +159,8 @@
     /** @hide */
     public Bundle toBundle() {
         Bundle b = new Bundle();
-        if (mPendingIntentBalAllowed != null) {
-            b.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, mPendingIntentBalAllowed);
+        if (mPendingIntentBalAllowed != MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+            b.putInt(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, mPendingIntentBalAllowed);
         }
         if (mPendingIntentBalAllowedByPermission) {
             b.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION,
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 0e20138..cd7f1e4 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -679,11 +679,13 @@
         if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
             if (mCancelable) {
                 cancel();
-            } else {
+                event.startTracking();
+                return true;
+            } else if (mWindow.shouldCloseOnTouchOutside()) {
                 dismiss();
+                event.startTracking();
+                return true;
             }
-            event.startTracking();
-            return true;
         }
 
         return false;
diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java
index ef6982e..4ac40a1 100644
--- a/core/java/android/app/DreamManager.java
+++ b/core/java/android/app/DreamManager.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 
+import android.annotation.FlaggedApi;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
@@ -30,6 +31,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.dreams.DreamService;
+import android.service.dreams.Flags;
 import android.service.dreams.IDreamManager;
 
 /**
@@ -217,4 +219,19 @@
         }
         return false;
     }
+
+    /**
+     * Sets whether the dream is obscured by something.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_DREAM_HANDLES_BEING_OBSCURED)
+    @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+    public void setDreamIsObscured(boolean isObscured) {
+        try {
+            mService.setDreamIsObscured(isObscured);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0caea7f..0672064 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5704,6 +5704,7 @@
             p.headerless(resId == getBaseLayoutResource()
                     || resId == getHeadsUpBaseLayoutResource()
                     || resId == getCompactHeadsUpBaseLayoutResource()
+                    || resId == getMessagingCompactHeadsUpLayoutResource()
                     || resId == getMessagingLayoutResource()
                     || resId == R.layout.notification_template_material_media);
             RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
@@ -6491,8 +6492,13 @@
         // visual regressions.
         @SuppressWarnings("AndroidFrameworkCompatChange")
         private boolean bigContentViewRequired() {
-            if (!Flags.notificationExpansionOptional()
-                    && mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) {
+            if (Flags.notificationExpansionOptional()) {
+                // Notifications without a bigContentView, style, or actions do not need to expand
+                boolean exempt = mN.bigContentView == null
+                        && mStyle == null && mActions.size() == 0;
+                return !exempt;
+            }
+            if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) {
                 return true;
             }
             // Notifications with contentView and without a bigContentView, style, or actions would
@@ -7155,13 +7161,7 @@
             // Adds any new extras provided by the user.
             if (mUserExtras != null) {
                 final Bundle saveExtras = (Bundle) mUserExtras.clone();
-                if (SystemProperties.getBoolean(
-                        "persist.sysui.notification.builder_extras_override", false)) {
-                    mN.extras.putAll(saveExtras);
-                } else {
-                    saveExtras.putAll(mN.extras);
-                    mN.extras = saveExtras;
-                }
+                mN.extras.putAll(saveExtras);
             }
 
             if (!Flags.sortSectionByTime()) {
@@ -7300,6 +7300,10 @@
             return R.layout.notification_template_material_compact_heads_up_base;
         }
 
+        private int getMessagingCompactHeadsUpLayoutResource() {
+            return R.layout.notification_template_material_messaging_compact_heads_up;
+        }
+
         private int getBigBaseLayoutResource() {
             return R.layout.notification_template_material_big_base;
         }
@@ -9172,10 +9176,78 @@
         @Nullable
         @Override
         public RemoteViews makeCompactHeadsUpContentView() {
-            // TODO(b/336229954): Apply minimal HUN treatment to Messaging Notifications.
-            return makeHeadsUpContentView(false);
+            final boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY;
+            Icon conversationIcon = null;
+            Notification.Action remoteInputAction = null;
+            if (isConversationLayout) {
+
+                conversationIcon = mShortcutIcon;
+
+                // conversation icon is m
+                // Extract the conversation icon for one to one conversations from
+                // the latest incoming message since
+                // fixTitleAndTextExtras also uses it as data source for title and text
+                if (conversationIcon == null && !mIsGroupConversation) {
+                    final Message message = findLatestIncomingMessage();
+                    if (message != null) {
+                        final Person sender = message.mSender;
+                        if (sender != null) {
+                            conversationIcon = sender.getIcon();
+                        }
+                    }
+                }
+
+                if (Flags.compactHeadsUpNotificationReply()) {
+                    // Get the first non-contextual inline reply action.
+                    final List<Notification.Action> nonContextualActions =
+                            mBuilder.getNonContextualActions();
+                    for (int i = 0; i < nonContextualActions.size(); i++) {
+                        final Notification.Action action = nonContextualActions.get(i);
+                        if (mBuilder.hasValidRemoteInput(action)) {
+                            remoteInputAction = action;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            // This method fills title and text
+            fixTitleAndTextExtras(mBuilder.mN.extras);
+            final StandardTemplateParams p = mBuilder.mParams.reset()
+                    .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP)
+                    .highlightExpander(isConversationLayout)
+                    .fillTextsFrom(mBuilder)
+                    .hideTime(true)
+                    .summaryText("");
+            p.headerTextSecondary(p.mText);
+            TemplateBindResult bindResult = new TemplateBindResult();
+
+            RemoteViews contentView = mBuilder.applyStandardTemplate(
+                    mBuilder.getMessagingCompactHeadsUpLayoutResource(), p, bindResult);
+            if (conversationIcon != null) {
+                contentView.setViewVisibility(R.id.icon, View.GONE);
+                contentView.setViewVisibility(R.id.conversation_icon, View.VISIBLE);
+                contentView.setBoolean(R.id.conversation_icon, "setApplyCircularCrop", true);
+                contentView.setImageViewIcon(R.id.conversation_icon, conversationIcon);
+            }
+
+            if (remoteInputAction != null) {
+                contentView.setViewVisibility(R.id.reply_action_container, View.VISIBLE);
+
+                final RemoteViews inlineReplyButton =
+                        mBuilder.generateActionButton(remoteInputAction, false, p);
+                // Clear the drawable
+                inlineReplyButton.setInt(R.id.action0, "setBackgroundResource", 0);
+                inlineReplyButton.setTextViewText(R.id.action0,
+                        mBuilder.mContext.getString(R.string.notification_compact_heads_up_reply));
+                contentView.addView(R.id.reply_action_container, inlineReplyButton);
+            } else {
+                contentView.setViewVisibility(R.id.reply_action_container, View.GONE);
+            }
+            return contentView;
         }
 
+
         /**
          * @hide
          */
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index b82a1e3..e4310c1 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -2972,6 +2972,7 @@
         android.Manifest.permission.INTERACT_ACROSS_USERS,
         android.Manifest.permission.ACCESS_NOTIFICATIONS})
     @FlaggedApi(android.service.notification.Flags.FLAG_CALLSTYLE_CALLBACK_API)
+    @SuppressLint("UserHandle")
     public void registerCallNotificationEventListener(@NonNull String packageName,
             @NonNull UserHandle userHandle, @NonNull @CallbackExecutor Executor executor,
             @NonNull CallNotificationEventListener listener) {
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 97c2e43..2e38c06 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -60,6 +60,9 @@
 # ComponentCaller
 per-file ComponentCaller.java = file:COMPONENT_CALLER_OWNERS
 
+# DreamManager
+per-file DreamManager.java = file:/DREAM_MANAGER_OWNERS
+
 # GrammaticalInflectionManager
 per-file *GrammaticalInflection* = file:/services/core/java/com/android/server/grammaticalinflection/OWNERS
 per-file grammatical_inflection_manager.aconfig = file:/services/core/java/com/android/server/grammaticalinflection/OWNERS
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 301fef8..14195c4 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -44,7 +43,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -59,7 +57,6 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.IUndoMediaTransferCallback;
 import com.android.internal.statusbar.NotificationVisibility;
-import com.android.internal.util.DataClass;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -632,49 +629,38 @@
     }
 
     /**
-     * @deprecated
-     * Disable some features in the status bar.  Pass the bitwise-or of the DISABLE_*
-     * flags. To re-enable everything, pass {@link #DISABLE_NONE}.
-     *
-     * This method is deprecated and callers should use
-     * {@link #requestDisabledComponent(DisableInfo, String)}
+     * Disable some features in the status bar.  Pass the bitwise-or of the DISABLE_* flags.
+     * To re-enable everything, pass {@link #DISABLE_NONE}.
      *
      * @hide
      */
-    @Deprecated
     @UnsupportedAppUsage
     public void disable(int what) {
-        requestDisabledComponent(new DisableInfo(what & DISABLE_MASK, what & DISABLE2_MASK), null);
-    }
-
-    /**
-     * @deprecated
-     * Disable some features in the status bar.  Pass the bitwise-or of the DISABLE_2*
-     * flags. To re-enable everything, pass {@link #DISABLE2_NONE}.
-     *
-     * This method is deprecated and callers should use
-     * {@link #requestDisabledComponent(DisableInfo, String)}
-     *
-     * @hide
-     */
-    @Deprecated
-    @UnsupportedAppUsage
-    public void disable2(int what) {
-        requestDisabledComponent(new DisableInfo(what & DISABLE_MASK, what & DISABLE2_MASK), null);
-    }
-
-    /**
-     * Disable some features in the status bar. Pass a DisableInfo object with the required flags.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public void requestDisabledComponent(DisableInfo disableInfo, String reason) {
         try {
             final int userId = Binder.getCallingUserHandle().getIdentifier();
             final IStatusBarService svc = getService();
             if (svc != null) {
-                svc.disableForUser(disableInfo, mToken, mContext.getPackageName(), userId, reason);
+                svc.disableForUser(what, mToken, mContext.getPackageName(), userId);
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
+     * To re-enable everything, pass {@link #DISABLE_NONE}.
+     *
+     * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
+     *
+     * @hide
+     */
+    public void disable2(@Disable2Flags int what) {
+        try {
+            final int userId = Binder.getCallingUserHandle().getIdentifier();
+            final IStatusBarService svc = getService();
+            if (svc != null) {
+                svc.disable2ForUser(what, mToken, mContext.getPackageName(), userId);
             }
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
@@ -902,9 +888,18 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
     public void setDisabledForSetup(boolean disabled) {
-        int flags1 = disabled ? DEFAULT_SETUP_DISABLE_FLAGS : DISABLE_NONE;
-        DisableInfo info = new DisableInfo(flags1, DISABLE2_NONE);
-        requestDisabledComponent(info, "setDisabledForSetup");
+        try {
+            final int userId = Binder.getCallingUserHandle().getIdentifier();
+            final IStatusBarService svc = getService();
+            if (svc != null) {
+                svc.disableForUser(disabled ? DEFAULT_SETUP_DISABLE_FLAGS : DISABLE_NONE,
+                        mToken, mContext.getPackageName(), userId);
+                svc.disable2ForUser(disabled ? DEFAULT_SETUP_DISABLE2_FLAGS : DISABLE2_NONE,
+                        mToken, mContext.getPackageName(), userId);
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -919,9 +914,16 @@
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
     public void setExpansionDisabledForSimNetworkLock(boolean disabled) {
-        int flags1 = disabled ? DEFAULT_SIM_LOCKED_DISABLED_FLAGS : DISABLE_NONE;
-        DisableInfo info = new DisableInfo(flags1, DISABLE2_NONE);
-        requestDisabledComponent(info, "setExpansionDisabledForSimNetworkLock");
+        try {
+            final int userId = Binder.getCallingUserHandle().getIdentifier();
+            final IStatusBarService svc = getService();
+            if (svc != null) {
+                svc.disableForUser(disabled ? DEFAULT_SIM_LOCKED_DISABLED_FLAGS : DISABLE_NONE,
+                        mToken, mContext.getPackageName(), userId);
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -1313,75 +1315,33 @@
      * @hide
      */
     @SystemApi
-    @DataClass
-    public static final class DisableInfo implements Parcelable {
+    public static final class DisableInfo {
 
-        /**
-         * @hide
-         */
         private boolean mStatusBarExpansion;
-        /**
-         * @hide
-         */
         private boolean mNavigateHome;
-        /**
-         * @hide
-         */
         private boolean mNotificationPeeking;
-        /**
-         * @hide
-         */
         private boolean mRecents;
-        /**
-         * @hide
-         */
-        private boolean mBack;
-        /**
-         * @hide
-         */
         private boolean mSearch;
-        /**
-         * @hide
-         */
         private boolean mSystemIcons;
-        /**
-         * @hide
-         */
         private boolean mClock;
-        /**
-         * @hide
-         */
         private boolean mNotificationIcons;
-        /**
-         * @hide
-         */
         private boolean mRotationSuggestion;
-        /**
-         * @hide
-         */
-        private boolean mNotificationTicker;
 
         /** @hide */
-        @SuppressLint("UnflaggedApi")
         public DisableInfo(int flags1, int flags2) {
             mStatusBarExpansion = (flags1 & DISABLE_EXPAND) != 0;
             mNavigateHome = (flags1 & DISABLE_HOME) != 0;
             mNotificationPeeking = (flags1 & DISABLE_NOTIFICATION_ALERTS) != 0;
             mRecents = (flags1 & DISABLE_RECENT) != 0;
-            mBack = (flags1 & DISABLE_BACK) != 0;
             mSearch = (flags1 & DISABLE_SEARCH) != 0;
             mSystemIcons = (flags1 & DISABLE_SYSTEM_INFO) != 0;
             mClock = (flags1 & DISABLE_CLOCK) != 0;
             mNotificationIcons = (flags1 & DISABLE_NOTIFICATION_ICONS) != 0;
-            mNotificationTicker = (flags1 & DISABLE_NOTIFICATION_TICKER) != 0;
             mRotationSuggestion = (flags2 & DISABLE2_ROTATE_SUGGESTIONS) != 0;
         }
 
         /** @hide */
-        @SuppressLint("UnflaggedApi")
-        public DisableInfo() {
-            setEnableAll();
-        }
+        public DisableInfo() {}
 
         /**
          * @return {@code true} if expanding the notification shade is disabled
@@ -1409,7 +1369,7 @@
         }
 
         /** * @hide */
-        public void setNavigationHomeDisabled(boolean disabled) {
+        public void setNagivationHomeDisabled(boolean disabled) {
             mNavigateHome = disabled;
         }
 
@@ -1444,20 +1404,6 @@
         }
 
         /**
-         * @return {@code true} if mBack is disabled
-         *
-         * @hide
-         */
-        public boolean isBackDisabled() {
-            return mBack;
-        }
-
-        /**  @hide */
-        public void setBackDisabled(boolean disabled) {
-            mBack = disabled;
-        }
-
-        /**
          * @return {@code true} if mSearch is disabled
          *
          * @hide
@@ -1515,20 +1461,6 @@
         }
 
         /**
-         * @return {@code true} if notification ticker is disabled
-         *
-         * @hide
-         */
-        public boolean isNotificationTickerDisabled() {
-            return mNotificationTicker;
-        }
-
-        /** * @hide */
-        public void setNotificationTickerDisabled(boolean disabled) {
-            mNotificationTicker = disabled;
-        }
-
-        /**
          * Returns whether the rotation suggestion is disabled.
          *
          * @hide
@@ -1538,11 +1470,6 @@
             return mRotationSuggestion;
         }
 
-        /** * @hide */
-        public void setRotationSuggestionDisabled(boolean disabled) {
-            mNotificationIcons = disabled;
-        }
-
         /**
          * @return {@code true} if no components are disabled (default state)
          * @hide
@@ -1550,8 +1477,8 @@
         @SystemApi
         public boolean areAllComponentsEnabled() {
             return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents
-                    && !mBack && !mSearch && !mSystemIcons && !mClock && !mNotificationIcons
-                    && !mNotificationTicker && !mRotationSuggestion;
+                    && !mSearch && !mSystemIcons && !mClock && !mNotificationIcons
+                    && !mRotationSuggestion;
         }
 
         /** @hide */
@@ -1560,12 +1487,10 @@
             mNavigateHome = false;
             mNotificationPeeking = false;
             mRecents = false;
-            mBack = false;
             mSearch = false;
             mSystemIcons = false;
             mClock = false;
             mNotificationIcons = false;
-            mNotificationTicker = false;
             mRotationSuggestion = false;
         }
 
@@ -1575,9 +1500,9 @@
          * @hide
          */
         public boolean areAllComponentsDisabled() {
-            return mStatusBarExpansion && mNavigateHome && mNotificationPeeking && mRecents && mBack
-                    && mSearch && mSystemIcons && mClock && mNotificationIcons
-                    && mNotificationTicker && mRotationSuggestion;
+            return mStatusBarExpansion && mNavigateHome && mNotificationPeeking
+                    && mRecents && mSearch && mSystemIcons && mClock && mNotificationIcons
+                    && mRotationSuggestion;
         }
 
         /** @hide */
@@ -1586,12 +1511,10 @@
             mNavigateHome = true;
             mNotificationPeeking = true;
             mRecents = true;
-            mBack = true;
             mSearch = true;
             mSystemIcons = true;
             mClock = true;
             mNotificationIcons = true;
-            mNotificationTicker = true;
             mRotationSuggestion = true;
         }
 
@@ -1599,19 +1522,16 @@
         @Override
         public String toString() {
             StringBuilder sb = new StringBuilder();
-
-            sb.append("Disable Info: ");
+            sb.append("DisableInfo: ");
             sb.append(" mStatusBarExpansion=").append(mStatusBarExpansion ? "disabled" : "enabled");
             sb.append(" mNavigateHome=").append(mNavigateHome ? "disabled" : "enabled");
             sb.append(" mNotificationPeeking=")
                     .append(mNotificationPeeking ? "disabled" : "enabled");
             sb.append(" mRecents=").append(mRecents ? "disabled" : "enabled");
-            sb.append(" mBack=").append(mBack ? "disabled" : "enabled");
             sb.append(" mSearch=").append(mSearch ? "disabled" : "enabled");
             sb.append(" mSystemIcons=").append(mSystemIcons ? "disabled" : "enabled");
             sb.append(" mClock=").append(mClock ? "disabled" : "enabled");
             sb.append(" mNotificationIcons=").append(mNotificationIcons ? "disabled" : "enabled");
-            sb.append(" mNotificationTicker=").append(mNotificationTicker ? "disabled" : "enabled");
             sb.append(" mRotationSuggestion=").append(mRotationSuggestion ? "disabled" : "enabled");
 
             return sb.toString();
@@ -1619,7 +1539,7 @@
         }
 
         /**
-         * Convert a DisableInfo to equivalent flags.
+         * Convert a DisableInfo to equivalent flags
          * @return a pair of equivalent disable flags
          *
          * @hide
@@ -1632,278 +1552,14 @@
             if (mNavigateHome) disable1 |= DISABLE_HOME;
             if (mNotificationPeeking) disable1 |= DISABLE_NOTIFICATION_ALERTS;
             if (mRecents) disable1 |= DISABLE_RECENT;
-            if (mBack) disable1 |= DISABLE_BACK;
             if (mSearch) disable1 |= DISABLE_SEARCH;
             if (mSystemIcons) disable1 |= DISABLE_SYSTEM_INFO;
             if (mClock) disable1 |= DISABLE_CLOCK;
             if (mNotificationIcons) disable1 |= DISABLE_NOTIFICATION_ICONS;
-            if (mNotificationTicker) disable1 |= DISABLE_NOTIFICATION_TICKER;
             if (mRotationSuggestion) disable2 |= DISABLE2_ROTATE_SUGGESTIONS;
 
             return new Pair<Integer, Integer>(disable1, disable2);
         }
-
-
-
-        // Code below generated by codegen v1.0.23.
-        //
-        // DO NOT MODIFY!
-        // CHECKSTYLE:OFF Generated code
-        //
-        // To regenerate run:
-        // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/StatusBarManager.java
-        //
-        // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-        //   Settings > Editor > Code Style > Formatter Control
-        //@formatter:off
-
-
-        /**
-         * Creates a new DisableInfo.
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public DisableInfo(
-                boolean statusBarExpansion,
-                boolean navigateHome,
-                boolean notificationPeeking,
-                boolean recents,
-                boolean back,
-                boolean search,
-                boolean systemIcons,
-                boolean clock,
-                boolean notificationIcons,
-                boolean rotationSuggestion,
-                boolean notificationTicker) {
-            this.mStatusBarExpansion = statusBarExpansion;
-            this.mNavigateHome = navigateHome;
-            this.mNotificationPeeking = notificationPeeking;
-            this.mRecents = recents;
-            this.mBack = back;
-            this.mSearch = search;
-            this.mSystemIcons = systemIcons;
-            this.mClock = clock;
-            this.mNotificationIcons = notificationIcons;
-            this.mRotationSuggestion = rotationSuggestion;
-            this.mNotificationTicker = notificationTicker;
-
-            // onConstructed(); // You can define this method to get a callback
-        }
-
-        /**
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public boolean isStatusBarExpansion() {
-            return mStatusBarExpansion;
-        }
-
-        /**
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public boolean isNavigateHome() {
-            return mNavigateHome;
-        }
-
-        /**
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public boolean isNotificationPeeking() {
-            return mNotificationPeeking;
-        }
-
-        /**
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public boolean isRecents() {
-            return mRecents;
-        }
-
-        /**
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public boolean isBack() {
-            return mBack;
-        }
-
-        /**
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public boolean isSearch() {
-            return mSearch;
-        }
-
-        /**
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public boolean isSystemIcons() {
-            return mSystemIcons;
-        }
-
-        /**
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public boolean isClock() {
-            return mClock;
-        }
-
-        /**
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public boolean isNotificationIcons() {
-            return mNotificationIcons;
-        }
-
-        /**
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public boolean isRotationSuggestion() {
-            return mRotationSuggestion;
-        }
-
-        /**
-         * @hide
-         */
-        @DataClass.Generated.Member
-        public boolean isNotificationTicker() {
-            return mNotificationTicker;
-        }
-
-        /**
-         * @hide
-         */
-        @SuppressLint("UnflaggedApi")
-        @Override
-        @DataClass.Generated.Member
-        public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
-            // You can override field parcelling by defining methods like:
-            // void parcelFieldName(Parcel dest, int flags) { ... }
-
-            int flg = 0;
-            if (mStatusBarExpansion) flg |= 0x1;
-            if (mNavigateHome) flg |= 0x2;
-            if (mNotificationPeeking) flg |= 0x4;
-            if (mRecents) flg |= 0x8;
-            if (mBack) flg |= 0x10;
-            if (mSearch) flg |= 0x20;
-            if (mSystemIcons) flg |= 0x40;
-            if (mClock) flg |= 0x80;
-            if (mNotificationIcons) flg |= 0x100;
-            if (mRotationSuggestion) flg |= 0x200;
-            if (mNotificationTicker) flg |= 0x400;
-            dest.writeInt(flg);
-        }
-
-        /**
-         * @hide
-         */
-        @SuppressLint("UnflaggedApi")
-        @Override
-        @DataClass.Generated.Member
-        public int describeContents() { return 0; }
-
-        /** @hide */
-        @SuppressWarnings({"unchecked", "RedundantCast"})
-        @DataClass.Generated.Member
-        /* package-private */ DisableInfo(@NonNull android.os.Parcel in) {
-            // You can override field unparcelling by defining methods like:
-            // static FieldType unparcelFieldName(Parcel in) { ... }
-
-            int flg = in.readInt();
-            boolean statusBarExpansion = (flg & 0x1) != 0;
-            boolean navigateHome = (flg & 0x2) != 0;
-            boolean notificationPeeking = (flg & 0x4) != 0;
-            boolean recents = (flg & 0x8) != 0;
-            boolean back = (flg & 0x10) != 0;
-            boolean search = (flg & 0x20) != 0;
-            boolean systemIcons = (flg & 0x40) != 0;
-            boolean clock = (flg & 0x80) != 0;
-            boolean notificationIcons = (flg & 0x100) != 0;
-            boolean rotationSuggestion = (flg & 0x200) != 0;
-            boolean notificationTicker = (flg & 0x400) != 0;
-
-            this.mStatusBarExpansion = statusBarExpansion;
-            this.mNavigateHome = navigateHome;
-            this.mNotificationPeeking = notificationPeeking;
-            this.mRecents = recents;
-            this.mBack = back;
-            this.mSearch = search;
-            this.mSystemIcons = systemIcons;
-            this.mClock = clock;
-            this.mNotificationIcons = notificationIcons;
-            this.mRotationSuggestion = rotationSuggestion;
-            this.mNotificationTicker = notificationTicker;
-
-            // onConstructed(); // You can define this method to get a callback
-        }
-
-        @DataClass.Generated.Member
-        public static final @NonNull Parcelable.Creator<DisableInfo> CREATOR
-                = new Parcelable.Creator<DisableInfo>() {
-            @Override
-            public DisableInfo[] newArray(int size) {
-                return new DisableInfo[size];
-            }
-
-            @Override
-            public DisableInfo createFromParcel(@NonNull android.os.Parcel in) {
-                return new DisableInfo(in);
-            }
-        };
-
-        @DataClass.Generated(
-                time = 1708625947132L,
-                codegenVersion = "1.0.23",
-                sourceFile = "frameworks/base/core/java/android/app/StatusBarManager.java",
-                inputSignatures = "private  boolean mStatusBarExpansion\nprivate  boolean "
-                        + "mNavigateHome\nprivate  boolean mNotificationPeeking\nprivate  "
-                        + "boolean mRecents\nprivate  boolean mBack\nprivate  boolean mSearch\n"
-                        + "private  boolean mSystemIcons\nprivate  boolean mClock\nprivate  "
-                        + "boolean mNotificationIcons\nprivate  boolean mRotationSuggestion\n"
-                        + "private  boolean mNotificationTicker\npublic "
-                        + "@android.annotation.SystemApi boolean isStatusBarExpansionDisabled()\n"
-                        + "public  void setStatusBarExpansionDisabled(boolean)\npublic "
-                        + "@android.annotation.SystemApi boolean isNavigateToHomeDisabled()\npublic"
-                        + "  void setNavigationHomeDisabled(boolean)\npublic "
-                        + "@android.annotation.SystemApi boolean isNotificationPeekingDisabled()"
-                        + "\npublic  void setNotificationPeekingDisabled(boolean)\npublic "
-                        + "@android.annotation.SystemApi boolean isRecentsDisabled()\npublic  "
-                        + "void setRecentsDisabled(boolean)\npublic  boolean isBackDisabled()"
-                        + "\npublic  void setBackDisabled(boolean)\npublic "
-                        + "@android.annotation.SystemApi boolean isSearchDisabled()\npublic  "
-                        + "void setSearchDisabled(boolean)\npublic  boolean "
-                        + "areSystemIconsDisabled()\npublic  void setSystemIconsDisabled(boolean)\n"
-                        + "public  boolean isClockDisabled()\npublic  "
-                        + "void setClockDisabled(boolean)\npublic  boolean "
-                        + "areNotificationIconsDisabled()\npublic  void "
-                        + "setNotificationIconsDisabled(boolean)\npublic  boolean "
-                        + "isNotificationTickerDisabled()\npublic  void "
-                        + "setNotificationTickerDisabled(boolean)\npublic "
-                        + "@android.annotation.TestApi boolean isRotationSuggestionDisabled()\n"
-                        + "public  void setRotationSuggestionDisabled(boolean)\npublic "
-                        + "@android.annotation.SystemApi boolean areAllComponentsEnabled()\npublic"
-                        + "  void setEnableAll()\npublic  boolean areAllComponentsDisabled()\n"
-                        + "public  void setDisableAll()\npublic @android.annotation.NonNull "
-                        + "@java.lang.Override java.lang.String toString()\npublic  "
-                        + "android.util.Pair<java.lang.Integer,java.lang.Integer> toFlags()\n"
-                        + "class DisableInfo extends java.lang.Object implements "
-                        + "[android.os.Parcelable]\n@com.android.internal.util.DataClass")
-        @Deprecated
-        private void __metadata() {}
-
-
-        //@formatter:on
-        // End of generated code
-
     }
 
     /**
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index a29c196..0deb842 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -378,6 +378,14 @@
                 }
             ],
             "file_patterns": ["(/|^)ContextImpl.java"]
+        },
+        {
+            "file_patterns": [
+                "(/|^)Activity.*.java",
+                "(/|^)PendingIntent.java",
+                "(/|^)ComtextImpl.java"
+            ],
+            "name": "CtsWindowManagerBackgroundActivityTestCases"
         }
     ],
     "postsubmit": [
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 69f29f3..c529f7d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1698,7 +1698,7 @@
 
     /**
      * A boolean extra indicating whether device encryption can be skipped as part of
-     * <a href="#managed-provisioning>provisioning</a>.
+     * <a href="#managed-provisioning">provisioning</a>.
      *
      * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} or an intent with action
      * {@link #ACTION_PROVISION_MANAGED_DEVICE} that starts device owner provisioning.
@@ -10427,7 +10427,7 @@
     @WorkerThread
     public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
             Bundle settings) {
-        if (!Flags.dmrhCanSetAppRestriction()) {
+        if (!Flags.dmrhSetAppRestrictions()) {
             throwIfParentInstance("setApplicationRestrictions");
         }
 
@@ -11835,7 +11835,7 @@
     @WorkerThread
     public @NonNull Bundle getApplicationRestrictions(
             @Nullable ComponentName admin, String packageName) {
-        if (!Flags.dmrhCanSetAppRestriction()) {
+        if (!Flags.dmrhSetAppRestrictions()) {
             throwIfParentInstance("getApplicationRestrictions");
         }
 
@@ -14120,7 +14120,7 @@
     public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
         throwIfParentInstance("getParentProfileInstance");
         try {
-            if (Flags.dmrhCanSetAppRestriction()) {
+            if (Flags.dmrhSetAppRestrictions()) {
                 UserManager um = mContext.getSystemService(UserManager.class);
                 if (!um.isManagedProfile()) {
                     throw new SecurityException("The current user does not have a parent profile.");
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 3d6ec19..4154e66 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -244,10 +244,13 @@
 }
 
 flag {
-  name: "dmrh_can_set_app_restriction"
+  name: "dmrh_set_app_restrictions"
   namespace: "enterprise"
   description: "Allow DMRH to set application restrictions (both on the profile and the parent)"
   bug: "328758346"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
 
 flag {
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 50c7b7f..6ceae17 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -160,3 +160,10 @@
   description: "[Minimal HUN] Enables the compact heads up notification feature"
   bug: "270709257"
 }
+
+flag {
+  name: "compact_heads_up_notification_reply"
+  namespace: "systemui"
+  description: "[Minimal HUN] Enables the compact heads up notification reply capability for Conversation Notifications"
+  bug: "336229954"
+}
\ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
index 5e1c1e0..a37f51b 100644
--- a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
+++ b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -529,7 +529,6 @@
      * {@link Bundle}s annotated with this type will be validated that they are in-effect read-only
      * when passed via Binder IPC. Following restrictions apply :
      * <ul>
-     * <li> No Nested Bundles are allowed.</li>
      * <li> {@link PersistableBundle}s are allowed.</li>
      * <li> Any primitive types or their collections can be added as usual.</li>
      * <li>IBinder objects should *not* be added.</li>
diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java
index 99fa869..1b718d4 100644
--- a/core/java/android/app/prediction/AppPredictionContext.java
+++ b/core/java/android/app/prediction/AppPredictionContext.java
@@ -24,6 +24,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * Class that provides contextual information about the environment in which the app prediction is
  * used, such as package name, UI in which the app targets are shown, and number of targets.
@@ -99,6 +101,13 @@
     }
 
     @Override
+    public int hashCode() {
+        int hashCode = Objects.hash(mUiSurface, mPackageName);
+        hashCode = 31 * hashCode + mPredictedTargetCount;
+        return hashCode;
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java
index fef9e70..25c1a59 100644
--- a/core/java/android/app/prediction/AppTarget.java
+++ b/core/java/android/app/prediction/AppTarget.java
@@ -167,6 +167,16 @@
     }
 
     @Override
+    public int hashCode() {
+        int hashCode = Objects.hash(mId, mPackageName, mClassName, mUser);
+        if (mShortcutInfo != null) {
+            hashCode = 31 * hashCode + mShortcutInfo.getId().hashCode();
+        }
+        hashCode = 31 * hashCode + mRank;
+        return hashCode;
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java
index 91da8ec..e36d878 100644
--- a/core/java/android/app/prediction/AppTargetEvent.java
+++ b/core/java/android/app/prediction/AppTargetEvent.java
@@ -24,6 +24,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * A representation of an app target event.
@@ -116,6 +117,13 @@
     }
 
     @Override
+    public int hashCode() {
+        int hashCode = Objects.hash(mTarget, mLocation);
+        hashCode = 31 * hashCode + mAction;
+        return hashCode;
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/app/prediction/OWNERS b/core/java/android/app/prediction/OWNERS
index fe012da..73168fb 100644
--- a/core/java/android/app/prediction/OWNERS
+++ b/core/java/android/app/prediction/OWNERS
@@ -1,2 +1,4 @@
+pinyaoting@google.com
+hyunyoungs@google.com
 adamcohen@google.com
 sunnygoyal@google.com
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index cda2867..9b53461 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -33,11 +33,13 @@
 import android.os.IBinder;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.window.ActivityWindowInfo;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.concurrent.RejectedExecutionException;
 import java.util.function.BiConsumer;
 
 /**
@@ -47,6 +49,8 @@
  */
 public class ClientTransactionListenerController {
 
+    private static final String TAG = "ClientTransactionListenerController";
+
     private static ClientTransactionListenerController sController;
 
     private final Object mLock = new Object();
@@ -179,10 +183,14 @@
         }
 
         // Dispatch the display changed callbacks.
-        final int displayCount = configUpdatedDisplayIds.size();
-        for (int i = 0; i < displayCount; i++) {
-            final int displayId = configUpdatedDisplayIds.valueAt(i);
-            onDisplayChanged(displayId);
+        try {
+            final int displayCount = configUpdatedDisplayIds.size();
+            for (int i = 0; i < displayCount; i++) {
+                final int displayId = configUpdatedDisplayIds.valueAt(i);
+                onDisplayChanged(displayId);
+            }
+        } catch (RejectedExecutionException e) {
+            Log.w(TAG, "Failed to notify DisplayListener because the Handler is shutting down");
         }
     }
 
@@ -222,7 +230,11 @@
         }
 
         if (changedDisplayId != INVALID_DISPLAY) {
-            onDisplayChanged(changedDisplayId);
+            try {
+                onDisplayChanged(changedDisplayId);
+            } catch (RejectedExecutionException e) {
+                Log.w(TAG, "Failed to notify DisplayListener because the Handler is shutting down");
+            }
         }
     }
 
@@ -235,9 +247,11 @@
     /**
      * Called when receives a {@link Configuration} changed event that is updating display-related
      * window configuration.
+     *
+     * @throws RejectedExecutionException if the display listener handler is closing.
      */
     @VisibleForTesting
-    public void onDisplayChanged(int displayId) {
+    public void onDisplayChanged(int displayId) throws RejectedExecutionException {
         mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
     }
 }
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 57b5c13..3213b40 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -1041,6 +1041,7 @@
      */
     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
         if (mService == null) {
+            Log.e(TAG, "Service wasn't initialized, appWidgetId=" + appWidgetId);
             return null;
         }
         try {
@@ -1048,6 +1049,9 @@
             if (info != null) {
                 // Converting complex to dp.
                 info.updateDimensions(mDisplayMetrics);
+            } else {
+                Log.e(TAG, "App widget provider info is null. PackageName=" + mPackageName
+                        + " appWidgetId-" + appWidgetId);
             }
             return info;
         } catch (RemoteException e) {
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index 8458857..36d0e08 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -39,3 +39,11 @@
     description: "Expose perm sync user consent API"
     bug: "309528663"
 }
+
+flag {
+    name: "ongoing_perm_sync"
+    is_exported: true
+    namespace: "companion"
+    description: "Enable ongoing perm sync"
+    bug: "338469649"
+}
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index 3e23762..b29b52d 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -127,4 +127,15 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
+}
+
+flag {
+  name: "impulse_velocity_strategy_for_touch_navigation"
+  is_exported: true
+  namespace: "virtual_devices"
+  description: "Use impulse velocity strategy during conversion of touch navigation flings into Dpad events"
+  bug: "338426241"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
\ No newline at end of file
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9e316a2..c8cae82 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4353,13 +4353,6 @@
             "android.intent.extra.BRIGHTNESS_DIALOG_IS_FULL_WIDTH";
 
     /**
-     * Activity Action: Shows the contrast setting dialog.
-     * @hide
-     */
-    public static final String ACTION_SHOW_CONTRAST_DIALOG =
-            "com.android.intent.action.SHOW_CONTRAST_DIALOG";
-
-    /**
      * Broadcast Action:  A global button was pressed.  Includes a single
      * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
      * caused the broadcast.
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index b074e8b..2e252c1 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -60,6 +60,10 @@
  * {@link android.app.PendingIntent#getIntentSender() PendingIntent.getIntentSender()}.
  */
 public class IntentSender implements Parcelable {
+    private static final Bundle SEND_INTENT_DEFAULT_OPTIONS =
+            ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT).toBundle();
+
     @UnsupportedAppUsage
     private final IIntentSender mTarget;
     IBinder mWhitelistToken;
@@ -161,7 +165,8 @@
      */
     public void sendIntent(Context context, int code, Intent intent,
             OnFinished onFinished, Handler handler) throws SendIntentException {
-        sendIntent(context, code, intent, onFinished, handler, null, null /* options */);
+        sendIntent(context, code, intent, onFinished, handler, null,
+                SEND_INTENT_DEFAULT_OPTIONS);
     }
 
     /**
@@ -194,7 +199,7 @@
             OnFinished onFinished, Handler handler, String requiredPermission)
             throws SendIntentException {
         sendIntent(context, code, intent, onFinished, handler, requiredPermission,
-                null /* options */);
+                SEND_INTENT_DEFAULT_OPTIONS);
     }
 
     /**
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
index a2cfbf5..41a4288 100644
--- a/core/java/android/content/TEST_MAPPING
+++ b/core/java/android/content/TEST_MAPPING
@@ -56,6 +56,10 @@
         }
       ],
       "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
+    },
+    {
+      "name": "CtsWindowManagerBackgroundActivityTestCases",
+      "file_patterns": ["(/|^)IntentSender.java"]
     }
   ],
   "ravenwood-presubmit": [
@@ -63,5 +67,7 @@
       "name": "CtsContentTestCasesRavenwood",
       "host": true
     }
+  ],
+  "postsubmit": [
   ]
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 83285e0..c506c97 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -270,11 +270,20 @@
     /**
      * Application level {@link android.content.pm.PackageManager.Property PackageManager
      * .Property} for a app to inform the installer that a file containing the app's android
-     * safety label data is bundled into the APK at the given path.
+     * safety label data is bundled into the APK as a raw resource.
+     *
+     * <p>For example:
+     * <pre>
+     * &lt;application&gt;
+     *   &lt;property
+     *     android:name="android.content.PROPERTY_ANDROID_SAFETY_LABEL"
+     *     android:resource="@raw/app-metadata"/&gt;
+     * &lt;/application&gt;
+     * </pre>
      * @hide
      */
-    public static final String PROPERTY_ANDROID_SAFETY_LABEL_PATH =
-            "android.content.SAFETY_LABEL_PATH";
+    public static final String PROPERTY_ANDROID_SAFETY_LABEL =
+            "android.content.PROPERTY_ANDROID_SAFETY_LABEL";
 
     /**
      * A property value set within the manifest.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4b579e7..1f6730b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2628,6 +2628,15 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                targetCode)) {
+            Slog.w(TAG, "Package requires development platform " + targetCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return Build.VERSION.SDK_INT;
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + targetCode
@@ -2699,6 +2708,15 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                minCode)) {
+            Slog.w(TAG, "Package requires min development platform " + minCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return Build.VERSION.SDK_INT;
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + minCode
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index cee8d96..061e7f7 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -254,6 +254,14 @@
 }
 
 flag {
+    name: "wait_application_killed"
+    namespace: "package_manager_service"
+    description: "Feature flag to control whether to wait until the application is killed when clear application data"
+    bug: "31009094"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "component_state_changed_metrics"
     namespace: "package_manager_service"
     description: "Feature flag to log the metrics when the component state is changed."
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 83742eb..e2a131c 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -247,3 +247,13 @@
     description: "Allow MAIN user to access blocked number provider"
     bug: "338579331"
 }
+
+flag {
+    name: "restrict_quiet_mode_credential_bug_fix_to_managed_profiles"
+    namespace: "profile_experiences"
+    description: "Use user states to check the state of quiet mode for managed profiles only"
+    bug: "332812630"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index 153dd9a..c7403c0 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -316,6 +316,15 @@
             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                        minCode)) {
+            Slog.w(TAG, "Parsed package requires min development platform " + minCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return input.success(Build.VERSION.SDK_INT);
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
@@ -368,19 +377,27 @@
             return input.success(targetVers);
         }
 
+        // If it's a pre-release SDK and the codename matches this platform, it
+        // definitely targets this SDK.
+        if (matchTargetCode(platformSdkCodenames, targetCode)) {
+            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+        }
+
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                        targetCode)) {
+            Slog.w(TAG, "Parsed package requires development platform " + targetCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return input.success(Build.VERSION.SDK_INT);
+        }
+
         try {
             if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
                 return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
             }
         } catch (IllegalArgumentException e) {
-            // isAtMost() throws it when encountering an older SDK codename
-            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage());
-        }
-
-        // If it's a pre-release SDK and the codename matches this platform, it
-        // definitely targets this SDK.
-        if (matchTargetCode(platformSdkCodenames, targetCode)) {
-            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK");
         }
 
         // Otherwise, we're looking at an incompatible pre-release SDK.
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index d683d72..1eb466c 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -731,6 +731,7 @@
      * commits, or is rolled back, either explicitly or by a call to
      * {@link #yieldIfContendedSafely}.
      */
+    // TODO(340874899) Provide an Executor overload
     public void beginTransactionWithListener(
             @Nullable SQLiteTransactionListener transactionListener) {
         beginTransaction(transactionListener, true);
@@ -760,6 +761,7 @@
      *            transaction begins, commits, or is rolled back, either
      *            explicitly or by a call to {@link #yieldIfContendedSafely}.
      */
+    // TODO(340874899) Provide an Executor overload
     public void beginTransactionWithListenerNonExclusive(
             @Nullable SQLiteTransactionListener transactionListener) {
         beginTransaction(transactionListener, false);
@@ -785,6 +787,8 @@
      *   }
      * </pre>
      */
+    // TODO(340874899) Provide an Executor overload
+    @SuppressLint("ExecutorRegistration")
     @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
     public void beginTransactionWithListenerReadOnly(
             @Nullable SQLiteTransactionListener transactionListener) {
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index a0e40f6..61f1ee1 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -34,6 +34,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.TestApi;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.graphics.Bitmap;
@@ -603,7 +604,6 @@
             mPromptInfo.setIsForLegacyFingerprintManager(sensorId);
             return this;
         }
-        // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java)
 
         /**
          * Set if emergency call button should show, for example if biometrics are
@@ -613,12 +613,33 @@
          * @hide
          */
         @NonNull
+        @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL})
         public Builder setShowEmergencyCallButton(boolean showEmergencyCallButton) {
             mPromptInfo.setShowEmergencyCallButton(showEmergencyCallButton);
             return this;
         }
 
         /**
+         * Set caller's component name for getting logo icon/description. This should only be used
+         * by ConfirmDeviceCredentialActivity, see b/337082634 for more context.
+         *
+         * @param componentNameForConfirmDeviceCredentialActivity set the component name for
+         *                                                        ConfirmDeviceCredentialActivity.
+         * @return This builder.
+         * @hide
+         */
+        @NonNull
+        @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL})
+        public Builder setComponentNameForConfirmDeviceCredentialActivity(
+                ComponentName componentNameForConfirmDeviceCredentialActivity) {
+            mPromptInfo.setComponentNameForConfirmDeviceCredentialActivity(
+                    componentNameForConfirmDeviceCredentialActivity);
+            return this;
+        }
+
+        // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java)
+
+        /**
          * Creates a {@link BiometricPrompt}.
          *
          * @return An instance of {@link BiometricPrompt}.
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 18b75c9..bb07b9b 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -19,6 +19,7 @@
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.graphics.Bitmap;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -56,6 +57,7 @@
     private boolean mIsForLegacyFingerprintManager = false;
     private boolean mShowEmergencyCallButton = false;
     private boolean mUseParentProfileForDeviceCredential = false;
+    private ComponentName mComponentNameForConfirmDeviceCredentialActivity = null;
 
     public PromptInfo() {
 
@@ -87,6 +89,8 @@
         mIsForLegacyFingerprintManager = in.readBoolean();
         mShowEmergencyCallButton = in.readBoolean();
         mUseParentProfileForDeviceCredential = in.readBoolean();
+        mComponentNameForConfirmDeviceCredentialActivity = in.readParcelable(
+                ComponentName.class.getClassLoader(), ComponentName.class);
     }
 
     public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -132,10 +136,11 @@
         dest.writeBoolean(mIsForLegacyFingerprintManager);
         dest.writeBoolean(mShowEmergencyCallButton);
         dest.writeBoolean(mUseParentProfileForDeviceCredential);
+        dest.writeParcelable(mComponentNameForConfirmDeviceCredentialActivity, 0);
     }
 
     // LINT.IfChange
-    public boolean containsTestConfigurations() {
+    public boolean requiresTestOrInternalPermission() {
         if (mIsForLegacyFingerprintManager
                 && mAllowedSensorIds.size() == 1
                 && !mAllowBackgroundAuthentication) {
@@ -148,11 +153,15 @@
             return true;
         } else if (mIgnoreEnrollmentState) {
             return true;
+        } else if (mShowEmergencyCallButton) {
+            return true;
+        } else if (mComponentNameForConfirmDeviceCredentialActivity != null) {
+            return true;
         }
         return false;
     }
 
-    public boolean containsPrivateApiConfigurations() {
+    public boolean requiresInternalPermission() {
         if (mDisallowBiometricsIfPolicyExists) {
             return true;
         } else if (mUseDefaultTitle) {
@@ -177,7 +186,7 @@
      * Currently, logo res, logo bitmap, logo description, PromptContentViewWithMoreOptions needs
      * this permission.
      */
-    public boolean containsAdvancedApiConfigurations() {
+    public boolean requiresAdvancedPermission() {
         if (mLogoRes != -1) {
             return true;
         } else if (mLogoBitmap != null) {
@@ -305,6 +314,12 @@
         mShowEmergencyCallButton = showEmergencyCallButton;
     }
 
+    public void setComponentNameForConfirmDeviceCredentialActivity(
+            ComponentName componentNameForConfirmDeviceCredentialActivity) {
+        mComponentNameForConfirmDeviceCredentialActivity =
+                componentNameForConfirmDeviceCredentialActivity;
+    }
+
     public void setUseParentProfileForDeviceCredential(
             boolean useParentProfileForDeviceCredential) {
         mUseParentProfileForDeviceCredential = useParentProfileForDeviceCredential;
@@ -417,6 +432,10 @@
         return mShowEmergencyCallButton;
     }
 
+    public ComponentName getComponentNameForConfirmDeviceCredentialActivity() {
+        return mComponentNameForConfirmDeviceCredentialActivity;
+    }
+
     private void checkOnlyOneLogoSet() {
         if (mLogoRes != -1 && mLogoBitmap != null) {
             throw new IllegalStateException(
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index a019612..708f8a1 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -39,7 +39,6 @@
 import android.content.pm.PackageManager;
 import android.graphics.Point;
 import android.hardware.CameraExtensionSessionStats;
-import android.hardware.CameraIdRemapping;
 import android.hardware.CameraStatus;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceListener;
@@ -838,7 +837,10 @@
         return new CameraExtensionCharacteristics(mContext, cameraId, characteristicsMap);
     }
 
-    private Map<String, CameraCharacteristics> getPhysicalIdToCharsMap(
+    /**
+     * @hide
+     */
+    public Map<String, CameraCharacteristics> getPhysicalIdToCharsMap(
             CameraCharacteristics chars) throws CameraAccessException {
         HashMap<String, CameraCharacteristics> physicalIdsToChars =
                 new HashMap<String, CameraCharacteristics>();
@@ -974,8 +976,6 @@
             final int oomScoreOffset, int rotationOverride) throws CameraAccessException {
         CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
         CameraDevice device = null;
-        Map<String, CameraCharacteristics> physicalIdsToChars =
-                getPhysicalIdToCharsMap(characteristics);
         synchronized (mLock) {
             ICameraDeviceUser cameraUser = null;
             CameraDevice.CameraDeviceSetup cameraDeviceSetup = null;
@@ -990,7 +990,7 @@
                         callback,
                         executor,
                         characteristics,
-                        physicalIdsToChars,
+                        this,
                         mContext.getApplicationInfo().targetSdkVersion,
                         mContext, cameraDeviceSetup);
             ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
@@ -1995,17 +1995,6 @@
     }
 
     /**
-     * Remaps Camera Ids in the CameraService.
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.CAMERA_INJECT_EXTERNAL_CAMERA)
-    public void remapCameraIds(@NonNull CameraIdRemapping cameraIdRemapping)
-            throws CameraAccessException, SecurityException, IllegalArgumentException {
-        CameraManagerGlobal.get().remapCameraIds(cameraIdRemapping);
-    }
-
-    /**
      * Injects session params into existing clients in the CameraService.
      *
      * @param cameraId       The camera id of client to inject session params into.
@@ -2116,13 +2105,6 @@
 
         private final Object mLock = new Object();
 
-        /**
-         * The active CameraIdRemapping. This will be used to refresh the cameraIdRemapping state
-         * in the CameraService every time we connect to it, including when the CameraService
-         * Binder dies and we reconnect to it.
-         */
-        @Nullable private CameraIdRemapping mActiveCameraIdRemapping;
-
         // Access only through getCameraService to deal with binder death
         private ICameraService mCameraService;
         private boolean mHasOpenCloseListenerPermission = false;
@@ -2274,41 +2256,6 @@
             } catch (RemoteException e) {
                 // Camera service died in all probability
             }
-
-            if (mActiveCameraIdRemapping != null) {
-                try {
-                    cameraService.remapCameraIds(mActiveCameraIdRemapping);
-                } catch (ServiceSpecificException e) {
-                    // Unexpected failure, ignore and continue.
-                    Log.e(TAG, "Unable to remap camera Ids in the camera service");
-                } catch (RemoteException e) {
-                    // Camera service died in all probability
-                }
-            }
-        }
-
-        /** Updates the cameraIdRemapping state in the CameraService. */
-        public void remapCameraIds(@NonNull CameraIdRemapping cameraIdRemapping)
-                throws CameraAccessException, SecurityException {
-            synchronized (mLock) {
-                ICameraService cameraService = getCameraService();
-                if (cameraService == null) {
-                    throw new CameraAccessException(
-                            CameraAccessException.CAMERA_DISCONNECTED,
-                            "Camera service is currently unavailable.");
-                }
-
-                try {
-                    cameraService.remapCameraIds(cameraIdRemapping);
-                    mActiveCameraIdRemapping = cameraIdRemapping;
-                } catch (ServiceSpecificException e) {
-                    throw ExceptionUtils.throwAsPublicException(e);
-                } catch (RemoteException e) {
-                    throw new CameraAccessException(
-                            CameraAccessException.CAMERA_DISCONNECTED,
-                            "Camera service is currently unavailable.");
-                }
-            }
         }
 
         /** Injects session params into an existing client for cameraid. */
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 7754e32..de26384 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2359,7 +2359,10 @@
      * FPS.</p>
      * <p>If the session configuration is not supported, the AE mode reported in the
      * CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p>
-     * <p>The application can observe the CapturerResult field
+     * <p>When this AE mode is enabled, the CaptureResult field
+     * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be present and not null. Otherwise, the
+     * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} field will not be present in the CaptureResult.</p>
+     * <p>The application can observe the CaptureResult field
      * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} to determine when low light boost is 'ACTIVE' or
      * 'INACTIVE'.</p>
      * <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 5765a73..1460515 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2819,6 +2819,8 @@
      * <p>When low light boost is enabled by setting the AE mode to
      * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light
      * boost when the light level threshold is exceeded.</p>
+     * <p>This field is present in the CaptureResult when the AE mode is set to
+     * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'. Otherwise, the field is not present.</p>
      * <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
      * indicate when it is not being applied by returning 'INACTIVE'.</p>
      * <p>This key will be absent from the CaptureResult if AE mode is not set to
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 81bb9ac..e2b409f 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -31,6 +31,7 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraExtensionCharacteristics;
+import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CameraOfflineSession;
 import android.hardware.camera2.CaptureFailure;
@@ -146,7 +147,8 @@
 
     private final String mCameraId;
     private final CameraCharacteristics mCharacteristics;
-    private final Map<String, CameraCharacteristics> mPhysicalIdsToChars;
+    private Map<String, CameraCharacteristics> mPhysicalIdsToChars;
+    private final CameraManager mCameraManager;
     private final int mTotalPartialCount;
     private final Context mContext;
 
@@ -341,11 +343,12 @@
 
     public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor,
                         CameraCharacteristics characteristics,
-                        Map<String, CameraCharacteristics> physicalIdsToChars,
+                        @NonNull CameraManager manager,
                         int appTargetSdkVersion,
                         Context ctx,
                         @Nullable CameraDevice.CameraDeviceSetup cameraDeviceSetup) {
-        if (cameraId == null || callback == null || executor == null || characteristics == null) {
+        if (cameraId == null || callback == null || executor == null || characteristics == null
+                || manager == null) {
             throw new IllegalArgumentException("Null argument given");
         }
         mCameraId = cameraId;
@@ -357,7 +360,7 @@
             mDeviceExecutor = executor;
         }
         mCharacteristics = characteristics;
-        mPhysicalIdsToChars = physicalIdsToChars;
+        mCameraManager = manager;
         mAppTargetSdkVersion = appTargetSdkVersion;
         mContext = ctx;
         mCameraDeviceSetup = cameraDeviceSetup;
@@ -379,6 +382,18 @@
         }
     }
 
+    private Map<String, CameraCharacteristics> getPhysicalIdToChars() {
+        if (mPhysicalIdsToChars == null) {
+            try {
+                mPhysicalIdsToChars = mCameraManager.getPhysicalIdToCharsMap(mCharacteristics);
+            } catch (CameraAccessException e) {
+                Log.e(TAG, "Unable to query the physical characteristics map!");
+            }
+        }
+
+        return mPhysicalIdsToChars;
+    }
+
     public CameraDeviceCallbacks getCallbacks() {
         return mCallbacks;
     }
@@ -1598,7 +1613,7 @@
             return true;
         }
 
-        for (Map.Entry<String, CameraCharacteristics> entry : mPhysicalIdsToChars.entrySet()) {
+        for (Map.Entry<String, CameraCharacteristics> entry : getPhysicalIdToChars().entrySet()) {
             configMap = entry.getValue().get(ck);
 
             if (configMap != null &&
@@ -2621,7 +2636,7 @@
     public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)
             throws CameraAccessException {
         HashMap<String, CameraCharacteristics> characteristicsMap = new HashMap<>(
-                mPhysicalIdsToChars);
+                getPhysicalIdToChars());
         characteristicsMap.put(mCameraId, mCharacteristics);
         boolean initializationFailed = true;
         IBinder token = new Binder(TAG + " : " + mNextSessionId++);
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index d340f3f..3f2ef84 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -732,6 +732,10 @@
 
     /**
      * Get statically configured sensor properties.
+     * @deprecated Generally unsafe to use, use
+     * {@link FaceManager#addAuthenticatorsRegisteredCallback} API instead.
+     * In most cases this method will work as expected, but during early boot up, it will be
+     * null/empty and there is no way for the caller to know when it's actual value is ready.
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 25bfb2a..2ded615 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -1189,6 +1189,10 @@
 
     /**
      * Get statically configured sensor properties.
+     * @deprecated Generally unsafe to use, use
+     * {@link FingerprintManager#addAuthenticatorsRegisteredCallback} API instead.
+     * In most cases this method will work as expected, but during early boot up, it will be
+     * null/empty and there is no way for the caller to know when it's actual value is ready.
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/usb/DeviceFilter.java b/core/java/android/hardware/usb/DeviceFilter.java
index 66b0a42..3a271b4 100644
--- a/core/java/android/hardware/usb/DeviceFilter.java
+++ b/core/java/android/hardware/usb/DeviceFilter.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.usb.flags.Flags;
 import android.service.usb.UsbDeviceFilterProto;
 import android.util.Slog;
 
@@ -57,9 +58,12 @@
     public final String mProductName;
     // USB device serial number string (or null for unspecified)
     public final String mSerialNumber;
+    // USB interface name (or null for unspecified). This will be used when matching devices using
+    // the available interfaces.
+    public final String mInterfaceName;
 
     public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
-            String manufacturer, String product, String serialnum) {
+            String manufacturer, String product, String serialnum, String interfaceName) {
         mVendorId = vid;
         mProductId = pid;
         mClass = clasz;
@@ -68,6 +72,7 @@
         mManufacturerName = manufacturer;
         mProductName = product;
         mSerialNumber = serialnum;
+        mInterfaceName = interfaceName;
     }
 
     public DeviceFilter(UsbDevice device) {
@@ -79,6 +84,7 @@
         mManufacturerName = device.getManufacturerName();
         mProductName = device.getProductName();
         mSerialNumber = device.getSerialNumber();
+        mInterfaceName = null;
     }
 
     public DeviceFilter(@NonNull DeviceFilter filter) {
@@ -90,6 +96,7 @@
         mManufacturerName = filter.mManufacturerName;
         mProductName = filter.mProductName;
         mSerialNumber = filter.mSerialNumber;
+        mInterfaceName = filter.mInterfaceName;
     }
 
     public static DeviceFilter read(XmlPullParser parser)
@@ -102,7 +109,7 @@
         String manufacturerName = null;
         String productName = null;
         String serialNumber = null;
-
+        String interfaceName = null;
         int count = parser.getAttributeCount();
         for (int i = 0; i < count; i++) {
             String name = parser.getAttributeName(i);
@@ -114,6 +121,8 @@
                 productName = value;
             } else if ("serial-number".equals(name)) {
                 serialNumber = value;
+            }  else if ("interface-name".equals(name)) {
+                interfaceName = value;
             } else {
                 int intValue;
                 int radix = 10;
@@ -144,7 +153,7 @@
         }
         return new DeviceFilter(vendorId, productId,
                 deviceClass, deviceSubclass, deviceProtocol,
-                manufacturerName, productName, serialNumber);
+                manufacturerName, productName, serialNumber, interfaceName);
     }
 
     public void write(XmlSerializer serializer) throws IOException {
@@ -173,13 +182,25 @@
         if (mSerialNumber != null) {
             serializer.attribute(null, "serial-number", mSerialNumber);
         }
+        if (mInterfaceName != null) {
+            serializer.attribute(null, "interface-name", mInterfaceName);
+        }
         serializer.endTag(null, "usb-device");
     }
 
-    private boolean matches(int clasz, int subclass, int protocol) {
-        return ((mClass == -1 || clasz == mClass) &&
-                (mSubclass == -1 || subclass == mSubclass) &&
-                (mProtocol == -1 || protocol == mProtocol));
+    private boolean matches(int usbClass, int subclass, int protocol) {
+        return ((mClass == -1 || usbClass == mClass)
+                && (mSubclass == -1 || subclass == mSubclass)
+                && (mProtocol == -1 || protocol == mProtocol));
+    }
+
+    private boolean matches(int usbClass, int subclass, int protocol, String interfaceName) {
+        if (Flags.enableInterfaceNameDeviceFilter()) {
+            return matches(usbClass, subclass, protocol)
+                    && (mInterfaceName == null || mInterfaceName.equals(interfaceName));
+        } else {
+            return matches(usbClass, subclass, protocol);
+        }
     }
 
     public boolean matches(UsbDevice device) {
@@ -204,7 +225,7 @@
         for (int i = 0; i < count; i++) {
             UsbInterface intf = device.getInterface(i);
             if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
-                    intf.getInterfaceProtocol())) return true;
+                    intf.getInterfaceProtocol(), intf.getName())) return true;
         }
 
         return false;
@@ -320,11 +341,12 @@
 
     @Override
     public String toString() {
-        return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
-                ",mClass=" + mClass + ",mSubclass=" + mSubclass +
-                ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
-                ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
-                "]";
+        return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId
+                + ",mClass=" + mClass + ",mSubclass=" + mSubclass
+                + ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName
+                + ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber
+                + ",mInterfaceName=" + mInterfaceName
+                + "]";
     }
 
     /**
diff --git a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
index 94df160..40e5ffb 100644
--- a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
@@ -16,3 +16,11 @@
     description: "Feature flag for the api to check if a port supports mode change"
     bug: "323470419"
 }
+
+flag {
+    name: "enable_interface_name_device_filter"
+    is_exported: true
+    namespace: "usb"
+    description: "Feature flag to enable interface name as a parameter for device filter"
+    bug: "312828160"
+}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 594ec18..334b231 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -173,6 +173,12 @@
     public static final String FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY = "low_power_standby";
     /** @hide */
     public static final String FIREWALL_CHAIN_NAME_BACKGROUND = "background";
+    /** @hide */
+    public static final String FIREWALL_CHAIN_NAME_METERED_ALLOW = "metered_allow";
+    /** @hide */
+    public static final String FIREWALL_CHAIN_NAME_METERED_DENY_USER = "metered_deny_user";
+    /** @hide */
+    public static final String FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN = "metered_deny_admin";
 
     private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
 
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 05a3e18..fedc97d 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1387,7 +1387,11 @@
          * @param scheme name or {@code null} if this is a relative Uri
          */
         public Builder scheme(String scheme) {
-            this.scheme = scheme;
+            if (scheme != null) {
+                this.scheme = scheme.replaceAll("://", "");
+            } else {
+                this.scheme = null;
+            }
             return this;
         }
 
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index d45a17f..91ad22f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -120,6 +120,8 @@
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * StorageManager is the interface to the systems storage service. The storage
@@ -2512,6 +2514,9 @@
         return userId * PER_USER_RANGE + projectId;
     }
 
+    private static final Pattern PATTERN_USER_ID = Pattern.compile(
+            "(?i)^/storage/emulated/([0-9]+)");
+
     /**
      * Let StorageManager know that the quota type for a file on external storage should
      * be updated. Android tracks quotas for various media types. Consequently, this should be
@@ -2541,26 +2546,35 @@
     @SystemApi
     public void updateExternalStorageFileQuotaType(@NonNull File path,
             @QuotaType int quotaType) throws IOException {
+        if (!path.exists()) return;
+
         long projectId;
         final String filePath = path.getCanonicalPath();
-        int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE;
-        // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are also
-        // returned by enabling FLAG_INCLUDE_SHARED_PROFILE.
-        if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
-            volFlags |= FLAG_INCLUDE_SHARED_PROFILE;
-        }
-        final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags);
-        final StorageVolume volume = getStorageVolume(availableVolumes, path);
-        if (volume == null) {
-            Log.w(TAG, "Failed to update quota type for " + filePath);
-            return;
-        }
-        if (!volume.isEmulated()) {
-            // We only support quota tracking on emulated filesystems
-            return;
+
+        final int userId;
+        final Matcher matcher = PATTERN_USER_ID.matcher(filePath);
+        if (matcher.find()) {
+            userId = Integer.parseInt(matcher.group(1));
+        } else {  // fallback
+            int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE;
+            // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are
+            // also returned by enabling FLAG_INCLUDE_SHARED_PROFILE.
+            if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
+                volFlags |= FLAG_INCLUDE_SHARED_PROFILE;
+            }
+            final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags);
+            final StorageVolume volume = getStorageVolume(availableVolumes, path);
+            if (volume == null) {
+                Log.w(TAG, "Failed to update quota type for " + filePath);
+                return;
+            }
+            if (!volume.isEmulated()) {
+                // We only support quota tracking on emulated filesystems
+                return;
+            }
+            userId = volume.getOwner().getIdentifier();
         }
 
-        final int userId = volume.getOwner().getIdentifier();
         if (userId < 0) {
             throw new IllegalStateException("Failed to update quota type for " + filePath);
         }
diff --git a/core/java/android/permission/OWNERS b/core/java/android/permission/OWNERS
index d2f4b50..857bacd 100644
--- a/core/java/android/permission/OWNERS
+++ b/core/java/android/permission/OWNERS
@@ -1,14 +1,13 @@
 # Bug component: 137825
 
-augale@google.com
 evanseverson@google.com
 fayey@google.com
 jaysullivan@google.com
 joecastro@google.com
-kvakil@google.com
 mrulhania@google.com
 ntmyren@google.com
 rmacgregor@google.com
 theianchen@google.com
 yutingfang@google.com
 zhanghai@google.com
+kiranmr@google.com
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 0e28560..2ca58d1 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -166,6 +166,16 @@
 }
 
 flag {
+    name: "finish_running_ops_for_killed_packages"
+    namespace: "permissions"
+    description: "Finish all appops for a dead app process"
+    bug: "234630570"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "runtime_permission_appops_mapping_enabled"
     is_fixed_read_only: true
     namespace: "permissions"
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 009713f..4f5b67c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11173,6 +11173,35 @@
                 "visual_query_accessibility_detection_enabled";
 
         /**
+         * Timeout to be used for unbinding to the configured remote
+         * {@link android.service.ondeviceintelligence.OnDeviceIntelligenceService} if there are no
+         * requests in the queue. A value of -1 represents to never unbind.
+         *
+         * @hide
+         */
+        public static final String ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS =
+                "on_device_intelligence_unbind_timeout_ms";
+
+
+        /**
+         * Timeout that represents maximum idle time before which a callback should be populated.
+         *
+         * @hide
+         */
+        public static final String ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS =
+                "on_device_intelligence_idle_timeout_ms";
+
+        /**
+         * Timeout to be used for unbinding to the configured remote
+         * {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} if there
+         * are no requests in the queue. A value of -1 represents to never unbind.
+         *
+         * @hide
+         */
+        public static final String ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS =
+                "on_device_inference_unbind_timeout_ms";
+
+        /**
          * Control whether Night display is currently activated.
          * @hide
          */
@@ -14909,6 +14938,17 @@
         public static final String DROPBOX_TAG_PREFIX = "dropbox:";
 
         /**
+         * Lines of kernel logs to include with system crash/ANR/etc. reports, as a
+         * prefix of the dropbox tag of the report type. For example,
+         * "kernel_logs_for_system_server_anr" controls the lines of kernel logs
+         * captured with system server ANR reports. 0 to disable.
+         *
+         * @hide
+         */
+        @Readable
+        public static final String ERROR_KERNEL_LOG_PREFIX = "kernel_logs_for_";
+
+        /**
          * Lines of logcat to include with system crash/ANR/etc. reports, as a
          * prefix of the dropbox tag of the report type. For example,
          * "logcat_for_system_server_anr" controls the lines of logcat captured
diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING
index d5ac7a7..2eb285d 100644
--- a/core/java/android/provider/TEST_MAPPING
+++ b/core/java/android/provider/TEST_MAPPING
@@ -8,6 +8,9 @@
                 }
             ]
         },
+	{
+	    "name": "CtsMediaProviderTestCases"
+	},
         {
             "name": "CalendarProviderTests"
         },
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 5f6bdbf..38ab590 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -18,7 +18,7 @@
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.service.dreams.Flags.dreamHandlesConfirmKeys;
-import static android.service.dreams.Flags.dreamTracksFocus;
+import static android.service.dreams.Flags.dreamHandlesBeingObscured;
 
 import android.annotation.FlaggedApi;
 import android.annotation.IdRes;
@@ -571,15 +571,6 @@
     /** {@inheritDoc} */
     @Override
     public void onWindowFocusChanged(boolean hasFocus) {
-        if (!dreamTracksFocus()) {
-            return;
-        }
-
-        try {
-            mDreamManager.onDreamFocusChanged(hasFocus);
-        } catch (RemoteException ex) {
-            // system server died
-        }
     }
 
     /** {@inheritDoc} */
@@ -1737,7 +1728,7 @@
 
         @Override
         public void comeToFront() {
-            if (!dreamTracksFocus()) {
+            if (!dreamHandlesBeingObscured()) {
                 return;
             }
 
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 85f0368..cf98bfe0 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -48,5 +48,6 @@
     void setSystemDreamComponent(in ComponentName componentName);
     void registerDreamOverlayService(in ComponentName componentName);
     void startDreamActivity(in Intent intent);
-    void onDreamFocusChanged(in boolean hasFocus);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)")
+    oneway void setDreamIsObscured(in boolean isObscured);
 }
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index a42eaff..54d950c 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -39,8 +39,11 @@
 }
 
 flag {
-  name: "dream_tracks_focus"
+  name: "dream_handles_being_obscured"
   namespace: "communal"
-  description: "This flag enables the ability for dreams to track whether or not they have focus"
-  bug: "331798001"
+  description: "This flag enables the ability for dreams to handle being obscured"
+  bug: "337302237"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 76889df..88da8eb 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -37,9 +38,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
-
 import com.android.internal.os.SomeArgs;
-
 import java.lang.annotation.Retention;
 import java.util.List;
 
@@ -116,6 +115,7 @@
      */
     protected Handler mHandler;
 
+    @SuppressLint("OnNameExpected")
     @Override
     protected void attachBaseContext(Context base) {
         super.attachBaseContext(base);
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index 07367df..caa0a9c 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.app.Flags;
 import android.app.RemoteInput;
@@ -229,6 +230,7 @@
     /**
      * Records that the user has replied to a notification that has a smart reply at least once.
      */
+    @SuppressLint("GetterSetterNames")
     @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
     public void setSmartReplied() {
         mSmartReplied = true;
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 1d7091c..910c462 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -1007,6 +1007,7 @@
         /**
          * Set whether priority channels are permitted to break through DND.
          */
+        @SuppressLint("BuilderSetStyle")
         @FlaggedApi(Flags.FLAG_MODES_API)
         public @NonNull Builder allowPriorityChannels(boolean allow) {
             mZenPolicy.mAllowChannels = allow ? CHANNEL_POLICY_PRIORITY : CHANNEL_POLICY_NONE;
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
index 29a6db6..8237b20 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
@@ -105,6 +105,21 @@
     public static final String SERVICE_INTERFACE =
             "android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService";
 
+    // TODO(339594686): make API
+    /**
+     * @hide
+     */
+    public static final String REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY =
+            "register_model_update_callback";
+    /**
+     * @hide
+     */
+    public static final String MODEL_LOADED_BUNDLE_KEY = "model_loaded";
+    /**
+     * @hide
+     */
+    public static final String MODEL_UNLOADED_BUNDLE_KEY = "model_unloaded";
+
     private IRemoteStorageService mRemoteStorageService;
 
     /**
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index d174bef..9589785 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1025,7 +1025,8 @@
             mWallpaperDimAmount = (!mShouldDimByDefault) ? mCustomDimAmount
                     : Math.max(mDefaultDimAmount, mCustomDimAmount);
 
-            if (!ENABLE_WALLPAPER_DIMMING || mBbqSurfaceControl == null
+            if (!ENABLE_WALLPAPER_DIMMING
+                    || mBbqSurfaceControl == null || !mBbqSurfaceControl.isValid()
                     || mWallpaperDimAmount == mPreviousWallpaperDimAmount) {
                 return;
             }
diff --git a/core/java/android/tracing/perfetto/DataSource.java b/core/java/android/tracing/perfetto/DataSource.java
index b9ab82c..4de7b62 100644
--- a/core/java/android/tracing/perfetto/DataSource.java
+++ b/core/java/android/tracing/perfetto/DataSource.java
@@ -85,7 +85,7 @@
                         new TracingContext<>(this, instanceIndex);
                 fun.trace(ctx);
 
-                ctx.flush();
+                nativeWritePackets(mNativeObj, ctx.getAndClearAllPendingTracePackets());
             } while (nativePerfettoDsTraceIterateNext(mNativeObj));
         } finally {
             nativePerfettoDsTraceIterateBreak(mNativeObj);
@@ -130,7 +130,8 @@
      * @param params Params to initialize the datasource with.
      */
     public void register(DataSourceParams params) {
-        nativeRegisterDataSource(this.mNativeObj, params.bufferExhaustedPolicy);
+        nativeRegisterDataSource(this.mNativeObj, params.bufferExhaustedPolicy,
+                params.willNotifyOnStop, params.noFlush);
     }
 
     /**
@@ -163,8 +164,8 @@
         return this.createInstance(inputStream, instanceIndex);
     }
 
-    private static native void nativeRegisterDataSource(
-            long dataSourcePtr, int bufferExhaustedPolicy);
+    private static native void nativeRegisterDataSource(long dataSourcePtr,
+            int bufferExhaustedPolicy, boolean willNotifyOnStop, boolean noFlush);
 
     private static native long nativeCreate(DataSource thiz, String name);
     private static native void nativeFlushAll(long nativeDataSourcePointer);
@@ -179,4 +180,6 @@
     private static native boolean nativePerfettoDsTraceIterateNext(long dataSourcePtr);
     private static native void nativePerfettoDsTraceIterateBreak(long dataSourcePtr);
     private static native int nativeGetPerfettoDsInstanceIndex(long dataSourcePtr);
+
+    private static native void nativeWritePackets(long dataSourcePtr, byte[][] packetData);
 }
diff --git a/core/java/android/tracing/perfetto/DataSourceParams.java b/core/java/android/tracing/perfetto/DataSourceParams.java
index 6cd04e3..e50f9d7 100644
--- a/core/java/android/tracing/perfetto/DataSourceParams.java
+++ b/core/java/android/tracing/perfetto/DataSourceParams.java
@@ -46,12 +46,67 @@
     // after a while.
     public static final int PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT = 1;
 
-    public static DataSourceParams DEFAULTS =
-            new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP);
+    public static DataSourceParams DEFAULTS = new DataSourceParams.Builder().build();
 
-    public DataSourceParams(@PerfettoDsBufferExhausted int bufferExhaustedPolicy) {
+    private DataSourceParams(@PerfettoDsBufferExhausted int bufferExhaustedPolicy,
+            boolean willNotifyOnStop, boolean noFlush) {
         this.bufferExhaustedPolicy = bufferExhaustedPolicy;
+        this.willNotifyOnStop = willNotifyOnStop;
+        this.noFlush = noFlush;
     }
 
     public final @PerfettoDsBufferExhausted int bufferExhaustedPolicy;
+    public final boolean willNotifyOnStop;
+    public final boolean noFlush;
+
+    /**
+     * DataSource Parameters builder
+     *
+     * @hide
+     */
+    public static final class Builder {
+        /**
+         * Specify behavior when running out of shared memory buffer space.
+         */
+        public Builder setBufferExhaustedPolicy(@PerfettoDsBufferExhausted int value) {
+            this.mBufferExhaustedPolicy = value;
+            return this;
+        }
+
+        /**
+         * If true, the data source is expected to ack the stop request through the
+         * NotifyDataSourceStopped() IPC. If false, the service won't wait for an ack.
+         * Set this parameter to false when dealing with potentially frozen producers
+         * that wouldn't be able to quickly ack the stop request.
+         *
+         * Default value: true
+         */
+        public Builder setWillNotifyOnStop(boolean value) {
+            this.mWillNotifyOnStop = value;
+            return this;
+        }
+
+        /**
+         * If true, the service won't emit flush requests for this data source. This
+         * allows the service to reduce the flush-related IPC traffic and better deal
+         * with frozen producers (see go/perfetto-frozen).
+         */
+        public Builder setNoFlush(boolean value) {
+            this.mNoFlush = value;
+            return this;
+        }
+
+        /**
+         * Build the DataSource parameters.
+         */
+        public DataSourceParams build() {
+            return new DataSourceParams(
+                    this.mBufferExhaustedPolicy, this.mWillNotifyOnStop, this.mNoFlush);
+        }
+
+        private @PerfettoDsBufferExhausted int mBufferExhaustedPolicy =
+                PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP;
+        private boolean mWillNotifyOnStop = true;
+        private boolean mNoFlush = false;
+    }
 }
diff --git a/core/java/android/tracing/perfetto/TracingContext.java b/core/java/android/tracing/perfetto/TracingContext.java
index 6b7df54..98cb4c8 100644
--- a/core/java/android/tracing/perfetto/TracingContext.java
+++ b/core/java/android/tracing/perfetto/TracingContext.java
@@ -59,19 +59,6 @@
     }
 
     /**
-     * Forces a commit of the thread-local tracing data written so far to the
-     * service. This is almost never required (tracing data is periodically
-     * committed as trace pages are filled up) and has a non-negligible
-     * performance hit (requires an IPC + refresh of the current thread-local
-     * chunk). The only case when this should be used is when handling OnStop()
-     * asynchronously, to ensure sure that the data is committed before the
-     * Stop timeout expires.
-     */
-    public void flush() {
-        nativeFlush(mDataSource.mNativeObj, getAndClearAllPendingTracePackets());
-    }
-
-    /**
      * Can optionally be used to store custom per-sequence
      * session data, which is not reset when incremental state is cleared
      * (e.g. configuration options).
@@ -109,7 +96,7 @@
         return incrementalState;
     }
 
-    private byte[][] getAndClearAllPendingTracePackets() {
+    protected byte[][] getAndClearAllPendingTracePackets() {
         byte[][] res = new byte[mTracePackets.size()][];
         for (int i = 0; i < mTracePackets.size(); i++) {
             ProtoOutputStream tracePacket = mTracePackets.get(i);
@@ -120,8 +107,6 @@
         return res;
     }
 
-    private static native void nativeFlush(long dataSourcePtr, byte[][] packetData);
-
     private static native Object nativeGetCustomTls(long nativeDsPtr);
     private static native void nativeSetCustomTls(long nativeDsPtr, Object tlsState);
 
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 35b137a..c5b6aa7 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -129,7 +129,7 @@
         }
         ViewGroup effective = null;
         ViewParent nextParent = focused.getParent();
-        do {
+        while (nextParent instanceof ViewGroup) {
             if (nextParent == root) {
                 return effective != null ? effective : root;
             }
@@ -143,7 +143,7 @@
                 effective = vg;
             }
             nextParent = nextParent.getParent();
-        } while (nextParent instanceof ViewGroup);
+        }
         return root;
     }
 
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 9ff29a8..4837ee5 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -26,11 +26,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UiContext;
+import android.app.Activity;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.StrictMode;
 import android.os.SystemClock;
@@ -299,6 +301,11 @@
     private VelocityTracker mVelocityTracker;
 
     /**
+     * Determines strategy for velocity calculation
+     */
+    private @VelocityTracker.VelocityTrackerStrategy int mVelocityTrackerStrategy;
+
+    /**
      * Consistency verifier for debugging purposes.
      */
     private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
@@ -347,17 +354,17 @@
 
     /**
      * Creates a GestureDetector with the supplied listener.
-     * This variant of the constructor should be used from a non-UI thread 
+     * This variant of the constructor should be used from a non-UI thread
      * (as it allows specifying the Handler).
-     * 
+     *
      * @param listener the listener invoked for all the callbacks, this must
      * not be null.
      * @param handler the handler to use
      *
      * @throws NullPointerException if {@code listener} is null.
      *
-     * @deprecated Use {@link #GestureDetector(android.content.Context,
-     *      android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead.
+     * @deprecated Use {@link #GestureDetector(Context, GestureDetector.OnGestureListener, Handler)}
+     * instead.
      */
     @Deprecated
     public GestureDetector(@NonNull OnGestureListener listener, @Nullable Handler handler) {
@@ -367,15 +374,14 @@
     /**
      * Creates a GestureDetector with the supplied listener.
      * You may only use this constructor from a UI thread (this is the usual situation).
-     * @see android.os.Handler#Handler()
-     * 
+     * @see Handler#Handler()
+     *
      * @param listener the listener invoked for all the callbacks, this must
      * not be null.
-     * 
+     *
      * @throws NullPointerException if {@code listener} is null.
      *
-     * @deprecated Use {@link #GestureDetector(android.content.Context,
-     *      android.view.GestureDetector.OnGestureListener)} instead.
+     * @deprecated Use {@link #GestureDetector(Context, GestureDetector.OnGestureListener)} instead.
      */
     @Deprecated
     public GestureDetector(@NonNull OnGestureListener listener) {
@@ -384,10 +390,10 @@
 
     /**
      * Creates a GestureDetector with the supplied listener.
-     * You may only use this constructor from a {@link android.os.Looper} thread.
-     * @see android.os.Handler#Handler()
+     * You may only use this constructor from a {@link Looper} thread.
+     * @see Handler#Handler()
      *
-     * @param context An {@link android.app.Activity} or a {@link Context} created from
+     * @param context An {@link Activity} or a {@link Context} created from
      * {@link Context#createWindowContext(int, Bundle)}
      * @param listener the listener invoked for all the callbacks, this must
      * not be null. If the listener implements the {@link OnDoubleTapListener} or
@@ -404,10 +410,10 @@
 
     /**
      * Creates a GestureDetector with the supplied listener that runs deferred events on the
-     * thread associated with the supplied {@link android.os.Handler}.
-     * @see android.os.Handler#Handler()
+     * thread associated with the supplied {@link Handler}.
+     * @see Handler#Handler()
      *
-     * @param context An {@link android.app.Activity} or a {@link Context} created from
+     * @param context An {@link Activity} or a {@link Context} created from
      * {@link Context#createWindowContext(int, Bundle)}
      * @param listener the listener invoked for all the callbacks, this must
      * not be null. If the listener implements the {@link OnDoubleTapListener} or
@@ -419,6 +425,31 @@
      */
     public GestureDetector(@Nullable @UiContext Context context,
             @NonNull OnGestureListener listener, @Nullable Handler handler) {
+        this(context, listener, handler, VelocityTracker.VELOCITY_TRACKER_STRATEGY_DEFAULT);
+    }
+
+    /**
+     * Creates a GestureDetector with the supplied listener that runs deferred events on the
+     * thread associated with the supplied {@link Handler}.
+     * @see Handler#Handler()
+     *
+     * @param context An {@link Activity} or a {@link Context} created from
+     * {@link Context#createWindowContext(int, Bundle)}
+     * @param listener the listener invoked for all the callbacks, this must
+     * not be null. If the listener implements the {@link OnDoubleTapListener} or
+     * {@link OnContextClickListener} then it will also be set as the listener for
+     * these callbacks (for example when using the {@link SimpleOnGestureListener}).
+     * @param handler the handler to use for running deferred listener events.
+     * @param velocityTrackerStrategy strategy to use for velocity calculation of scroll/fling
+     *                                events.
+     *
+     * @throws NullPointerException if {@code listener} is null.
+     *
+     * @hide
+     */
+    public GestureDetector(@Nullable @UiContext Context context,
+            @NonNull OnGestureListener listener, @Nullable Handler handler,
+            @VelocityTracker.VelocityTrackerStrategy int velocityTrackerStrategy) {
         if (handler != null) {
             mHandler = new GestureHandler(handler);
         } else {
@@ -431,15 +462,16 @@
         if (listener instanceof OnContextClickListener) {
             setContextClickListener((OnContextClickListener) listener);
         }
+        mVelocityTrackerStrategy = velocityTrackerStrategy;
         init(context);
     }
-    
+
     /**
      * Creates a GestureDetector with the supplied listener that runs deferred events on the
-     * thread associated with the supplied {@link android.os.Handler}.
-     * @see android.os.Handler#Handler()
+     * thread associated with the supplied {@link Handler}.
+     * @see Handler#Handler()
      *
-     * @param context An {@link android.app.Activity} or a {@link Context} created from
+     * @param context An {@link Activity} or a {@link Context} created from
      * {@link Context#createWindowContext(int, Bundle)}
      * @param listener the listener invoked for all the callbacks, this must
      * not be null.
@@ -547,7 +579,7 @@
         mCurrentMotionEvent = MotionEvent.obtain(ev);
 
         if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
+            mVelocityTracker = VelocityTracker.obtain(mVelocityTrackerStrategy);
         }
         mVelocityTracker.addMovement(ev);
 
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index 9503f49..8c14de6 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -210,18 +210,9 @@
             mInsetsController.setPredictiveBackImeHideAnimInProgress(true);
             notifyHideIme();
         }
-        if (mStartRootScrollY != 0) {
-            // RootView is panned, ensure that it is scrolled back to the intended scroll position
-            if (triggerBack) {
-                // requesting ime as invisible
-                mInsetsController.setRequestedVisibleTypes(0, ime());
-                // changes the animation state and notifies RootView of changed insets, which
-                // causes it to reset its scrollY to 0f (animated)
-                mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
-            } else {
-                // This causes RootView to update its scroll back to the panned position
-                mInsetsController.getHost().notifyInsetsChanged();
-            }
+        if (mStartRootScrollY != 0 && !triggerBack) {
+            // This causes RootView to update its scroll back to the panned position
+            mInsetsController.getHost().notifyInsetsChanged();
         }
     }
 
@@ -237,6 +228,12 @@
         // the IME away
         mInsetsController.getHost().getInputMethodManager()
                 .notifyImeHidden(mInsetsController.getHost().getWindowToken(), statsToken);
+
+        // requesting IME as invisible during post-commit
+        mInsetsController.setRequestedVisibleTypes(0, ime());
+        // Changes the animation state. This also notifies RootView of changed insets, which causes
+        // it to reset its scrollY to 0f (animated) if it was panned
+        mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
     }
 
     private void reset() {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index f1cb410..d7f2b01 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1262,10 +1262,13 @@
                     mHost.getInputMethodManager(), null /* icProto */);
         }
 
+        final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_USER,
+                ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION,
+                mHost.isHandlingPointerEvent() /* fromUser */);
         controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
                 interpolator, animationType,
                 getLayoutInsetsDuringAnimationMode(types, fromPredictiveBack),
-                false /* useInsetsAnimationThread */, null /* statsToken */);
+                false /* useInsetsAnimationThread */, statsToken);
     }
 
     private void controlAnimationUnchecked(@InsetsType int types,
@@ -1567,7 +1570,9 @@
             return;
         }
         final ImeTracker.Token statsToken = runner.getStatsToken();
-        if (shown) {
+        if (runner.getAnimationType() == ANIMATION_TYPE_USER) {
+            ImeTracker.forLogging().onUserFinished(statsToken, shown);
+        } else if (shown) {
             ImeTracker.forLogging().onProgress(statsToken,
                     ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_SHOW);
             ImeTracker.forLogging().onShown(statsToken);
@@ -1838,6 +1843,9 @@
             Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0);
             mHost.dispatchWindowInsetsAnimationStart(animation, bounds);
             mStartingAnimation = true;
+            if (runner.getAnimationType() == ANIMATION_TYPE_USER) {
+                ImeTracker.forLogging().onDispatched(runner.getStatsToken());
+            }
             runner.setReadyDispatched(true);
             listener.onReady(runner, types);
             mStartingAnimation = false;
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index d31f823..27176a4 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -264,7 +264,6 @@
 
     /**
      * Obtains a velocity tracker with the specified strategy.
-     * For testing and comparison purposes only.
      *
      * @param strategy The strategy Id, VELOCITY_TRACKER_STRATEGY_DEFAULT to use the default.
      * @return The velocity tracker.
@@ -272,6 +271,9 @@
      * @hide
      */
     public static VelocityTracker obtain(int strategy) {
+        if (strategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) {
+            return obtain();
+        }
         return new VelocityTracker(strategy);
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 155c053..0715474 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -427,12 +427,6 @@
 
     private static final long NANOS_PER_SEC = 1000000000;
 
-    // If the ViewRootImpl has been idle for more than 200ms, clear the preferred
-    // frame rate category and frame rate.
-    private static final int IDLE_TIME_MILLIS = 250;
-
-    private static final long NANOS_PER_MILLI = 1_000_000;
-
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
 
@@ -665,8 +659,6 @@
     private int mMinusOneFrameIntervalMillis = 0;
     // VRR interval between the previous and the frame before
     private int mMinusTwoFrameIntervalMillis = 0;
-    // VRR has the invalidation idle message been posted?
-    private boolean mInvalidationIdleMessagePosted = false;
 
     /**
      * Update the Choreographer's FrameInfo object with the timing information for the current
@@ -929,15 +921,6 @@
     private String mFrameRateCategoryView;
 
     /**
-     * The resolved pointer icon type requested by this window.
-     * A null value indicates the resolved pointer icon has not yet been calculated.
-     */
-    // TODO(b/293587049): Remove pointer icon tracking by type when refactor is complete.
-    @Nullable
-    private Integer mPointerIconType = null;
-    private PointerIcon mCustomPointerIcon = null;
-
-    /**
      * The resolved pointer icon requested by this window.
      * A null value indicates the resolved pointer icon has not yet been calculated.
      */
@@ -4278,10 +4261,6 @@
         // when the values are applicable.
         if (mDrawnThisFrame) {
             mDrawnThisFrame = false;
-            if (!mInvalidationIdleMessagePosted) {
-                mInvalidationIdleMessagePosted = true;
-                mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
-            }
             setCategoryFromCategoryCounts();
             updateInfrequentCount();
             setPreferredFrameRate(mPreferredFrameRate);
@@ -6442,7 +6421,6 @@
     private static final int MSG_SYNTHESIZE_INPUT_EVENT = 24;
     private static final int MSG_DISPATCH_WINDOW_SHOWN = 25;
     private static final int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26;
-    private static final int MSG_UPDATE_POINTER_ICON = 27;
     private static final int MSG_POINTER_CAPTURE_CHANGED = 28;
     private static final int MSG_INSETS_CONTROL_CHANGED = 29;
     private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 30;
@@ -6507,8 +6485,6 @@
                     return "MSG_SYNTHESIZE_INPUT_EVENT";
                 case MSG_DISPATCH_WINDOW_SHOWN:
                     return "MSG_DISPATCH_WINDOW_SHOWN";
-                case MSG_UPDATE_POINTER_ICON:
-                    return "MSG_UPDATE_POINTER_ICON";
                 case MSG_POINTER_CAPTURE_CHANGED:
                     return "MSG_POINTER_CAPTURE_CHANGED";
                 case MSG_INSETS_CONTROL_CHANGED:
@@ -6523,8 +6499,6 @@
                     return "MSG_WINDOW_TOUCH_MODE_CHANGED";
                 case MSG_KEEP_CLEAR_RECTS_CHANGED:
                     return "MSG_KEEP_CLEAR_RECTS_CHANGED";
-                case MSG_CHECK_INVALIDATION_IDLE:
-                    return "MSG_CHECK_INVALIDATION_IDLE";
                 case MSG_REFRESH_POINTER_ICON:
                     return "MSG_REFRESH_POINTER_ICON";
                 case MSG_TOUCH_BOOST_TIMEOUT:
@@ -6761,10 +6735,6 @@
                     final int deviceId = msg.arg1;
                     handleRequestKeyboardShortcuts(receiver, deviceId);
                 } break;
-                case MSG_UPDATE_POINTER_ICON: {
-                    MotionEvent event = (MotionEvent) msg.obj;
-                    resetPointerIcon(event);
-                } break;
                 case MSG_POINTER_CAPTURE_CHANGED: {
                     final boolean hasCapture = msg.arg1 != 0;
                     handlePointerCaptureChanged(hasCapture);
@@ -6789,30 +6759,6 @@
                     mNumPausedForSync = 0;
                     scheduleTraversals();
                     break;
-                case MSG_CHECK_INVALIDATION_IDLE: {
-                    long delta;
-                    if (mIsTouchBoosting || mIsFrameRateBoosting || mInsetsAnimationRunning) {
-                        delta = 0;
-                    } else {
-                        delta = System.nanoTime() / NANOS_PER_MILLI - mLastUpdateTimeMillis;
-                    }
-                    if (delta >= IDLE_TIME_MILLIS) {
-                        mFrameRateCategoryHighCount = 0;
-                        mFrameRateCategoryHighHintCount = 0;
-                        mFrameRateCategoryNormalCount = 0;
-                        mFrameRateCategoryLowCount = 0;
-                        mPreferredFrameRate = 0;
-                        mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
-                        setPreferredFrameRateCategory(FRAME_RATE_CATEGORY_NO_PREFERENCE);
-                        setPreferredFrameRate(0f);
-                        mInvalidationIdleMessagePosted = false;
-                    } else {
-                        mInvalidationIdleMessagePosted = true;
-                        mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
-                                IDLE_TIME_MILLIS - delta);
-                    }
-                    break;
-                }
                 case MSG_TOUCH_BOOST_TIMEOUT:
                     /**
                      * Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
@@ -7874,14 +7820,12 @@
                     || action == MotionEvent.ACTION_HOVER_EXIT) {
                 // Other apps or the window manager may change the icon type outside of
                 // this app, therefore the icon type has to be reset on enter/exit event.
-                mPointerIconType = null;
                 mResolvedPointerIcon = null;
             }
 
             if (action != MotionEvent.ACTION_HOVER_EXIT) {
                 // Resolve the pointer icon
                 if (!updatePointerIcon(event) && action == MotionEvent.ACTION_HOVER_MOVE) {
-                    mPointerIconType = null;
                     mResolvedPointerIcon = null;
                 }
             }
@@ -7944,12 +7888,6 @@
         return mAttachInfo.mHandlingPointerEvent;
     }
 
-    private void resetPointerIcon(MotionEvent event) {
-        mPointerIconType = null;
-        mResolvedPointerIcon = null;
-        updatePointerIcon(event);
-    }
-
 
     /**
      * If there is pointer that is showing a PointerIcon in this window, refresh the icon for that
@@ -8609,48 +8547,55 @@
 
         private int mPendingKeyMetaState;
 
-        private final GestureDetector mGestureDetector = new GestureDetector(mContext,
-                new GestureDetector.OnGestureListener() {
-                    @Override
-                    public boolean onDown(@NonNull MotionEvent e) {
-                        // This can be ignored since it's not clear what KeyEvent this will
-                        // belong to.
-                        return true;
-                    }
-
-                    @Override
-                    public void onShowPress(@NonNull MotionEvent e) {
-
-                    }
-
-                    @Override
-                    public boolean onSingleTapUp(@NonNull MotionEvent e) {
-                        dispatchTap(e.getEventTime());
-                        return true;
-                    }
-
-                    @Override
-                    public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
-                            float distanceX, float distanceY) {
-                        // Scroll doesn't translate to DPAD events so should be ignored.
-                        return true;
-                    }
-
-                    @Override
-                    public void onLongPress(@NonNull MotionEvent e) {
-                        // Long presses don't translate to DPAD events so should be ignored.
-                    }
-
-                    @Override
-                    public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
-                            float velocityX, float velocityY) {
-                        dispatchFling(velocityX, velocityY, e2.getEventTime());
-                        return true;
-                    }
-                });
+        private final GestureDetector mGestureDetector;
 
         SyntheticTouchNavigationHandler() {
             super(true);
+            int gestureDetectorVelocityStrategy =
+                    android.companion.virtual.flags.Flags
+                            .impulseVelocityStrategyForTouchNavigation()
+                    ? VelocityTracker.VELOCITY_TRACKER_STRATEGY_IMPULSE
+                    : VelocityTracker.VELOCITY_TRACKER_STRATEGY_DEFAULT;
+            mGestureDetector = new GestureDetector(mContext,
+                    new GestureDetector.OnGestureListener() {
+                        @Override
+                        public boolean onDown(@NonNull MotionEvent e) {
+                            // This can be ignored since it's not clear what KeyEvent this will
+                            // belong to.
+                            return true;
+                        }
+
+                        @Override
+                        public void onShowPress(@NonNull MotionEvent e) {
+                        }
+
+                        @Override
+                        public boolean onSingleTapUp(@NonNull MotionEvent e) {
+                            dispatchTap(e.getEventTime());
+                            return true;
+                        }
+
+                        @Override
+                        public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
+                                float distanceX, float distanceY) {
+                            // Scroll doesn't translate to DPAD events so should be ignored.
+                            return true;
+                        }
+
+                        @Override
+                        public void onLongPress(@NonNull MotionEvent e) {
+                            // Long presses don't translate to DPAD events so should be ignored.
+                        }
+
+                        @Override
+                        public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
+                                float velocityX, float velocityY) {
+                            dispatchFling(velocityX, velocityY, e2.getEventTime());
+                            return true;
+                        }
+                    },
+                    /* handler= */ null,
+                    gestureDetectorVelocityStrategy);
         }
 
         public void process(MotionEvent event) {
@@ -13034,10 +12979,6 @@
     private void removeVrrMessages() {
         mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
         mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
-        if (mInvalidationIdleMessagePosted) {
-            mInvalidationIdleMessagePosted = false;
-            mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE);
-        }
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index f454a6a..3091bf4 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -754,6 +754,19 @@
         }
     }
 
+    /** @see com.android.server.inputmethod.ImeTrackerService#onDispatched */
+    static void onDispatched(@NonNull ImeTracker.Token statsToken) {
+        final IImeTracker service = getImeTrackerService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.onDispatched(statsToken);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @see com.android.server.inputmethod.ImeTrackerService#hasPendingImeVisibilityRequests */
     @AnyThread
     @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index d992feb..edc9921 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -71,24 +71,40 @@
     /** The type of the IME request. */
     @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_SHOW,
-            TYPE_HIDE
+            TYPE_HIDE,
+            TYPE_USER,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface Type {}
 
-    /** IME show request type. */
+    /**
+     * IME show request type.
+     *
+     * @see android.view.InsetsController#ANIMATION_TYPE_SHOW
+     */
     int TYPE_SHOW = ImeProtoEnums.TYPE_SHOW;
 
-    /** IME hide request type. */
+    /**
+     * IME hide request type.
+     *
+     * @see android.view.InsetsController#ANIMATION_TYPE_HIDE
+     */
     int TYPE_HIDE = ImeProtoEnums.TYPE_HIDE;
 
+    /**
+     * IME user-controlled animation request type.
+     *
+     * @see android.view.InsetsController#ANIMATION_TYPE_USER
+     */
+    int TYPE_USER = ImeProtoEnums.TYPE_USER;
+
     /** The status of the IME request. */
     @IntDef(prefix = { "STATUS_" }, value = {
             STATUS_RUN,
             STATUS_CANCEL,
             STATUS_FAIL,
             STATUS_SUCCESS,
-            STATUS_TIMEOUT
+            STATUS_TIMEOUT,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface Status {}
@@ -117,7 +133,7 @@
     @IntDef(prefix = { "ORIGIN_" }, value = {
             ORIGIN_CLIENT,
             ORIGIN_SERVER,
-            ORIGIN_IME
+            ORIGIN_IME,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface Origin {}
@@ -400,20 +416,36 @@
     void onCancelled(@Nullable Token token, @Phase int phase);
 
     /**
-     * Called when the IME show request is successful.
+     * Called when the show IME request is successful.
      *
      * @param token the token tracking the current IME request or {@code null} otherwise.
      */
     void onShown(@Nullable Token token);
 
     /**
-     * Called when the IME hide request is successful.
+     * Called when the hide IME request is successful.
      *
      * @param token the token tracking the current IME request or {@code null} otherwise.
      */
     void onHidden(@Nullable Token token);
 
     /**
+     * Called when the user-controlled IME request was dispatched to the requesting app. The
+     * user animation can take an undetermined amount of time, so it shouldn't be tracked.
+     *
+     * @param token the token tracking the current IME request or {@code null} otherwise.
+     */
+    void onDispatched(@Nullable Token token);
+
+    /**
+     * Called when the animation of the user-controlled IME request finished.
+     *
+     * @param token the token tracking the current IME request or {@code null} otherwise.
+     * @param shown whether the end state of the animation was shown or hidden.
+     */
+    void onUserFinished(@Nullable Token token, boolean shown);
+
+    /**
      * Returns whether the current IME request was created due to a user interaction. This can
      * only be {@code true} when running on the view's UI thread.
      *
@@ -482,13 +514,6 @@
         /** Whether the stack trace at the request call site should be logged. */
         private boolean mLogStackTrace;
 
-        private void reloadSystemProperties() {
-            mLogProgress = SystemProperties.getBoolean(
-                    "persist.debug.imetracker", false);
-            mLogStackTrace = SystemProperties.getBoolean(
-                    "persist.debug.imerequest.logstacktrace", false);
-        }
-
         @NonNull
         @Override
         public Token onStart(@NonNull String component, int uid, @Type int type, @Origin int origin,
@@ -497,7 +522,7 @@
             final var token = IInputMethodManagerGlobalInvoker.onStart(tag, uid, type,
                     origin, reason, fromUser);
 
-            Log.i(TAG, token.mTag + ": onRequest" + (type == TYPE_SHOW ? "Show" : "Hide")
+            Log.i(TAG, token.mTag + ": " + getOnStartPrefix(type)
                     + " at " + Debug.originToString(origin)
                     + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason)
                     + " fromUser " + fromUser,
@@ -552,6 +577,45 @@
 
             Log.i(TAG, token.mTag + ": onHidden");
         }
+
+        @Override
+        public void onDispatched(@Nullable Token token) {
+            if (token == null) return;
+            IInputMethodManagerGlobalInvoker.onDispatched(token);
+
+            Log.i(TAG, token.mTag + ": onDispatched");
+        }
+
+        @Override
+        public void onUserFinished(@Nullable Token token, boolean shown) {
+            if (token == null) return;
+            // This is already sent to ImeTrackerService to mark it finished during onDispatched.
+
+            Log.i(TAG, token.mTag + ": onUserFinished " + (shown ? "shown" : "hidden"));
+        }
+
+        /**
+         * Gets the prefix string for {@link #onStart} based on the given request type.
+         *
+         * @param type request type for which to create the prefix string with.
+         */
+        @NonNull
+        private static String getOnStartPrefix(@Type int type) {
+            return switch (type) {
+                case TYPE_SHOW -> "onRequestShow";
+                case TYPE_HIDE -> "onRequestHide";
+                case TYPE_USER -> "onRequestUser";
+                default -> "onRequestUnknown";
+            };
+        }
+
+        /** Reloads the system properties related to this class. */
+        private void reloadSystemProperties() {
+            mLogProgress = SystemProperties.getBoolean(
+                    "persist.debug.imetracker", false);
+            mLogStackTrace = SystemProperties.getBoolean(
+                    "persist.debug.imerequest.logstacktrace", false);
+        }
     };
 
     /** The singleton IME tracker instance for instrumenting jank metrics. */
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 7885cd9..11ee286 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -54,6 +54,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -431,6 +432,14 @@
      * @hide
      */
     public InputMethodInfo(InputMethodInfo source) {
+        this(source, Collections.emptyList());
+    }
+
+    /**
+     * @hide
+     */
+    public InputMethodInfo(@NonNull InputMethodInfo source,
+            @NonNull List<InputMethodSubtype> additionalSubtypes) {
         mId = source.mId;
         mSettingsActivityName = source.mSettingsActivityName;
         mLanguageSettingsActivityName = source.mLanguageSettingsActivityName;
@@ -445,7 +454,19 @@
         mIsVrOnly = source.mIsVrOnly;
         mIsVirtualDeviceOnly = source.mIsVirtualDeviceOnly;
         mService = source.mService;
-        mSubtypes = source.mSubtypes;
+        if (additionalSubtypes.isEmpty()) {
+            mSubtypes = source.mSubtypes;
+        } else {
+            final ArrayList<InputMethodSubtype> subtypes = source.mSubtypes.toList();
+            final int additionalSubtypeCount = additionalSubtypes.size();
+            for (int i = 0; i < additionalSubtypeCount; ++i) {
+                final InputMethodSubtype additionalSubtype = additionalSubtypes.get(i);
+                if (!subtypes.contains(additionalSubtype)) {
+                    subtypes.add(additionalSubtype);
+                }
+            }
+            mSubtypes = new InputMethodSubtypeArray(subtypes);
+        }
         mHandledConfigChanges = source.mHandledConfigChanges;
         mSupportsStylusHandwriting = source.mSupportsStylusHandwriting;
         mSupportsConnectionlessStylusHandwriting = source.mSupportsConnectionlessStylusHandwriting;
diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
index c243a22..e2d343f 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
@@ -25,6 +25,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.GZIPOutputStream;
@@ -163,6 +164,18 @@
     }
 
     /**
+     * @return A list of {@link InputMethodInfo} copied from this array.
+     */
+    @NonNull
+    public ArrayList<InputMethodSubtype> toList() {
+        final ArrayList<InputMethodSubtype> list = new ArrayList<>(mCount);
+        for (int i = 0; i < mCount; ++i) {
+            list.add(get(i));
+        }
+        return list;
+    }
+
+    /**
      * Return the number of {@link InputMethodSubtype} objects.
      */
     public int getCount() {
diff --git a/core/java/android/widget/flags/notification_widget_flags.aconfig b/core/java/android/widget/flags/notification_widget_flags.aconfig
index 3a39631..503e542 100644
--- a/core/java/android/widget/flags/notification_widget_flags.aconfig
+++ b/core/java/android/widget/flags/notification_widget_flags.aconfig
@@ -47,3 +47,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+  name: "messaging_child_request_layout"
+  namespace: "systemui"
+  description: "MessagingChild always needs to be measured during MessagingLinearLayout onMeasure."
+  bug: "324537506"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 31e3a34..af3d7eb 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -56,6 +56,8 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.policy.DecorView;
 
+import java.io.Closeable;
+import java.io.IOException;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.function.Consumer;
@@ -568,6 +570,12 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         releaseAnimationSurfaceHost();
+        if (mIconView instanceof ImageView imageView
+                && imageView.getDrawable() instanceof Closeable closeableDrawable) {
+            try {
+                closeableDrawable.close();
+            } catch (IOException ignore) { }
+        }
     }
 
     @Override
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index d0f3c3e..6f7660a 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -84,8 +84,8 @@
     /**
      * Sets the activity navigation to be isolated, where the activity navigation on the
      * TaskFragment is separated from the rest activities in the Task. Activities cannot be
-     * started on an isolated TaskFragment unless the activities are launched from the same
-     * TaskFragment or explicitly requested to.
+     * started on an isolated TaskFragment unless explicitly requested to. That said, new launched
+     * activities should be positioned as a sibling to the TaskFragment with higher z-ordering.
      */
     public static final int OP_TYPE_SET_ISOLATED_NAVIGATION = 11;
 
@@ -149,6 +149,18 @@
      */
     public static final int OP_TYPE_SET_DECOR_SURFACE_BOOSTED = 18;
 
+    /**
+     * Sets the TaskFragment to be pinned.
+     * <p>
+     * If a TaskFragment is pinned, the TaskFragment should be the top-most TaskFragment among other
+     * sibling TaskFragments. Any newly launched and embeddable activity should not be placed in the
+     * pinned TaskFragment, unless the activity is launched from the pinned TaskFragment or
+     * explicitly requested to. Non-embeddable activities are not restricted to.
+     * <p>
+     * See {@link #OP_TYPE_REORDER_TO_FRONT} on how to reorder a pinned TaskFragment to the top.
+     */
+    public static final int OP_TYPE_SET_PINNED = 19;
+
     @IntDef(prefix = { "OP_TYPE_" }, value = {
             OP_TYPE_UNKNOWN,
             OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -170,6 +182,7 @@
             OP_TYPE_SET_DIM_ON_TASK,
             OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH,
             OP_TYPE_SET_DECOR_SURFACE_BOOSTED,
+            OP_TYPE_SET_PINNED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface OperationType {}
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java
index a77c234..1555416 100644
--- a/core/java/android/window/TaskFragmentParentInfo.java
+++ b/core/java/android/window/TaskFragmentParentInfo.java
@@ -18,6 +18,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
 import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 import android.os.Parcel;
@@ -27,10 +29,13 @@
 import java.util.Objects;
 
 /**
- * The information about the parent Task of a particular TaskFragment
+ * The information about the parent Task of a particular TaskFragment.
+ *
  * @hide
  */
-public class TaskFragmentParentInfo implements Parcelable {
+@SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages.
+@TestApi
+public final class TaskFragmentParentInfo implements Parcelable {
     @NonNull
     private final Configuration mConfiguration = new Configuration();
 
@@ -42,6 +47,7 @@
 
     @Nullable private final SurfaceControl mDecorSurface;
 
+    /** @hide */
     public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
             boolean visible, boolean hasDirectActivity, @Nullable SurfaceControl decorSurface) {
         mConfiguration.setTo(configuration);
@@ -51,6 +57,7 @@
         mDecorSurface = decorSurface;
     }
 
+    /** @hide */
     public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
         mConfiguration.setTo(info.getConfiguration());
         mDisplayId = info.mDisplayId;
@@ -59,7 +66,11 @@
         mDecorSurface = info.mDecorSurface;
     }
 
-    /** The {@link Configuration} of the parent Task */
+    /**
+     * The {@link Configuration} of the parent Task
+     *
+     * @hide
+     */
     @NonNull
     public Configuration getConfiguration() {
         return mConfiguration;
@@ -68,19 +79,27 @@
     /**
      * The display ID of the parent Task. {@link android.view.Display#INVALID_DISPLAY} means the
      * Task is detached from previously associated display.
+     *
+     * @hide
      */
     public int getDisplayId() {
         return mDisplayId;
     }
 
-    /** Whether the parent Task is visible or not */
+    /**
+     * Whether the parent Task is visible or not
+     *
+     * @hide
+     */
     public boolean isVisible() {
         return mVisible;
     }
 
     /**
      * Whether the parent Task has any direct child activity, which is not embedded in any
-     * TaskFragment, or not
+     * TaskFragment, or not.
+     *
+     * @hide
      */
     public boolean hasDirectActivity() {
         return mHasDirectActivity;
@@ -93,6 +112,8 @@
      * {@link com.android.server.wm.WindowOrganizerController#configurationsAreEqualForOrganizer(
      * Configuration, Configuration)} to determine if this {@link TaskFragmentParentInfo} should
      * be dispatched to the client.
+     *
+     * @hide
      */
     public boolean equalsForTaskFragmentOrganizer(@Nullable TaskFragmentParentInfo that) {
         if (that == null) {
@@ -103,6 +124,7 @@
                 && mDecorSurface == that.mDecorSurface;
     }
 
+    /** @hide */
     @Nullable
     public SurfaceControl getDecorSurface() {
         return mDecorSurface;
@@ -156,6 +178,7 @@
         return result;
     }
 
+    @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages.
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         mConfiguration.writeToParcel(dest, flags);
@@ -173,6 +196,8 @@
         mDecorSurface = in.readTypedObject(SurfaceControl.CREATOR);
     }
 
+    @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages.
+    @NonNull
     public static final Creator<TaskFragmentParentInfo> CREATOR =
             new Creator<TaskFragmentParentInfo>() {
                 @Override
@@ -186,6 +211,7 @@
                 }
             };
 
+    @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages.
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
index 4dada10..32e3f5a 100644
--- a/core/java/android/window/TaskFragmentTransaction.java
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -24,7 +24,6 @@
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -248,13 +247,6 @@
             return this;
         }
 
-        // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release.
-        /** Configuration of the parent Task. */
-        @NonNull
-        public Change setTaskConfiguration(@NonNull Configuration configuration) {
-            return this;
-        }
-
         /**
          * If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction}
          * from the {@link TaskFragmentOrganizer}, it may come with an error callback token to
@@ -299,12 +291,11 @@
             return this;
         }
 
-        // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release.
         /**
          * Sets info of the parent Task of the embedded TaskFragment.
          * @see TaskFragmentParentInfo
          *
-         * @hide pending unhide
+         * @hide
          */
         @NonNull
         public Change setTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
@@ -338,12 +329,6 @@
             return mTaskId;
         }
 
-        // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release.
-        @Nullable
-        public Configuration getTaskConfiguration() {
-            return mTaskFragmentParentInfo.getConfiguration();
-        }
-
         @Nullable
         public IBinder getErrorCallbackToken() {
             return mErrorCallbackToken;
@@ -365,8 +350,10 @@
             return mActivityToken;
         }
 
-        // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release.
-        /** @hide pending unhide */
+        /**
+         * Obtains the {@link TaskFragmentParentInfo} for this transaction.
+         */
+        @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages.
         @Nullable
         public TaskFragmentParentInfo getTaskFragmentParentInfo() {
             return mTaskFragmentParentInfo;
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index 41b6d31..a2e3d40 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -16,6 +16,7 @@
 
 package android.window;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
@@ -32,6 +33,9 @@
 import android.view.Surface;
 import android.view.WindowInsetsController;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Represents a task snapshot.
  * @hide
@@ -68,6 +72,21 @@
     private final boolean mHasImeSurface;
     // Must be one of the named color spaces, otherwise, always use SRGB color space.
     private final ColorSpace mColorSpace;
+    private int mInternalReferences;
+
+    /** This snapshot object is being broadcast. */
+    public static final int REFERENCE_BROADCAST = 1;
+    /** This snapshot object is in the cache. */
+    public static final int REFERENCE_CACHE = 1 << 1;
+    /** This snapshot object is being persistent. */
+    public static final int REFERENCE_PERSIST = 1 << 2;
+    @IntDef(flag = true, prefix = { "REFERENCE_" }, value = {
+            REFERENCE_BROADCAST,
+            REFERENCE_CACHE,
+            REFERENCE_PERSIST
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ReferenceFlags {}
 
     public TaskSnapshot(long id, long captureTime,
             @NonNull ComponentName topActivityComponent, HardwareBuffer snapshot,
@@ -296,7 +315,28 @@
                 + " mWindowingMode=" + mWindowingMode
                 + " mAppearance=" + mAppearance
                 + " mIsTranslucent=" + mIsTranslucent
-                + " mHasImeSurface=" + mHasImeSurface;
+                + " mHasImeSurface=" + mHasImeSurface
+                + " mInternalReferences=" + mInternalReferences;
+    }
+
+    /**
+     * Adds a reference when the object is held somewhere.
+     * Only used in core.
+     */
+    public synchronized void addReference(@ReferenceFlags int usage) {
+        mInternalReferences |= usage;
+    }
+
+    /**
+     * Removes a reference when the object is not held from somewhere. The snapshot will be closed
+     * once the reference becomes zero.
+     * Only used in core.
+     */
+    public synchronized void removeReference(@ReferenceFlags int usage) {
+        mInternalReferences &= ~usage;
+        if (mInternalReferences == 0 && mSnapshot != null && !mSnapshot.isClosed()) {
+            mSnapshot.close();
+        }
     }
 
     public static final @NonNull Creator<TaskSnapshot> CREATOR = new Creator<TaskSnapshot>() {
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 64fe66e..ec4e3e9 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -25,6 +25,7 @@
 import android.app.ActivityManager;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.WindowManager;
@@ -180,6 +181,7 @@
 
         public @ContainerOrder int mOrder = CONTAINER_ORDER_ANY;
         public ComponentName mTopActivity;
+        public IBinder mLaunchCookie;
 
         public Requirement() {
         }
@@ -193,6 +195,7 @@
             mMustBeTask = in.readBoolean();
             mOrder = in.readInt();
             mTopActivity = in.readTypedObject(ComponentName.CREATOR);
+            mLaunchCookie = in.readStrongBinder();
         }
 
         /** Go through changes and find if at-least one change matches this filter */
@@ -231,6 +234,9 @@
                 if (mMustBeTask && change.getTaskInfo() == null) {
                     continue;
                 }
+                if (!matchesCookie(change.getTaskInfo())) {
+                    continue;
+                }
                 return true;
             }
             return false;
@@ -247,13 +253,25 @@
             return false;
         }
 
+        private boolean matchesCookie(ActivityManager.RunningTaskInfo info) {
+            if (mLaunchCookie == null) return true;
+            if (info == null) return false;
+            for (IBinder cookie : info.launchCookies) {
+                if (mLaunchCookie.equals(cookie)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         /** Check if the request matches this filter. It may generate false positives */
         boolean matches(@NonNull TransitionRequestInfo request) {
             // Can't check modes/order since the transition hasn't been built at this point.
             if (mActivityType == ACTIVITY_TYPE_UNDEFINED) return true;
             return request.getTriggerTask() != null
                     && request.getTriggerTask().getActivityType() == mActivityType
-                    && matchesTopActivity(request.getTriggerTask(), null /* activityCmp */);
+                    && matchesTopActivity(request.getTriggerTask(), null /* activityCmp */)
+                    && matchesCookie(request.getTriggerTask());
         }
 
         @Override
@@ -267,6 +285,7 @@
             dest.writeBoolean(mMustBeTask);
             dest.writeInt(mOrder);
             dest.writeTypedObject(mTopActivity, flags);
+            dest.writeStrongBinder(mLaunchCookie);
         }
 
         @NonNull
@@ -307,6 +326,7 @@
             out.append(" mustBeTask=" + mMustBeTask);
             out.append(" order=" + containerOrderToString(mOrder));
             out.append(" topActivity=").append(mTopActivity);
+            out.append(" launchCookie=").append(mLaunchCookie);
             out.append("}");
             return out.toString();
         }
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index cd13c4a..4b2beb9 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -9,6 +9,16 @@
 }
 
 flag {
+  name: "disable_thin_letterboxing_policy"
+  namespace: "large_screen_experiences_app_compat"
+  description: "Whether reachability is disabled in case of thin letterboxing"
+  bug: "341027847"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "allows_screen_size_decoupled_from_status_bar_and_cutout"
   namespace: "large_screen_experiences_app_compat"
   description: "When necessary, configuration decoupled from status bar and display cutout"
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 6864bf7..8e18f84 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -24,6 +24,7 @@
 
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__DISABLED;
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__ENABLED;
@@ -32,6 +33,7 @@
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON_LONG_PRESS;
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__QUICK_SETTINGS;
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TWO_FINGER_TRIPLE_TAP;
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
@@ -48,7 +50,6 @@
 import android.content.Context;
 import android.provider.Settings;
 
-import com.android.internal.accessibility.common.ShortcutConstants;
 import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
 import com.android.internal.util.FrameworkStatsLog;
 
@@ -248,6 +249,8 @@
                 }
             case HARDWARE:
                 return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
+            case QUICK_SETTINGS:
+                return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__QUICK_SETTINGS;
         }
         return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
     }
diff --git a/core/java/com/android/internal/content/om/OverlayConfigParser.java b/core/java/com/android/internal/content/om/OverlayConfigParser.java
index faaf7d5..8132652 100644
--- a/core/java/com/android/internal/content/om/OverlayConfigParser.java
+++ b/core/java/com/android/internal/content/om/OverlayConfigParser.java
@@ -24,6 +24,8 @@
 import android.content.pm.PackagePartitions.SystemPartition;
 import android.os.Build;
 import android.os.FileUtils;
+import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Xml;
@@ -241,6 +243,18 @@
         }
     }
 
+    @FunctionalInterface
+    public interface SysPropWrapper{
+        /**
+         * Get system property
+         *
+         * @param property the key to look up.
+         *
+         * @return The property value if found, empty string otherwise.
+         */
+        String get(String property);
+    }
+
     /**
      * Retrieves overlays configured within the partition in increasing priority order.
      *
@@ -320,6 +334,76 @@
     }
 
     /**
+     * Expand the property inside a rro configuration path.
+     *
+     * A RRO configuration can contain a property, this method expands
+     * the property to its value.
+     *
+     * Only read only properties allowed, prefixed with ro. Other
+     * properties will raise exception.
+     *
+     * Only a single property in the path is allowed.
+     *
+     * Example "${ro.boot.hardware.sku}/config.xml" would expand to
+     *     "G020N/config.xml"
+     *
+     * @param configPath path to expand
+     * @param sysPropWrapper method used for reading properties
+     *
+     * @return The expanded path. Returns null if configPath is null.
+     */
+    @VisibleForTesting
+    public static String expandProperty(String configPath,
+            SysPropWrapper sysPropWrapper) {
+        if (configPath == null) {
+            return null;
+        }
+
+        int propStartPos = configPath.indexOf("${");
+        if (propStartPos == -1) {
+            // No properties inside the string, return as is
+            return configPath;
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append(configPath.substring(0, propStartPos));
+
+        // Read out the end position
+        int propEndPos = configPath.indexOf("}", propStartPos);
+        if (propEndPos == -1) {
+            throw new IllegalStateException("Malformed property, unmatched braces, in: "
+                    + configPath);
+        }
+
+        // Confirm that there is only one property inside the string
+        if (configPath.indexOf("${", propStartPos + 2) != -1) {
+            throw new IllegalStateException("Only a single property supported in path: "
+                    + configPath);
+        }
+
+        final String propertyName = configPath.substring(propStartPos + 2, propEndPos);
+        if (!propertyName.startsWith("ro.")) {
+            throw new IllegalStateException("Only read only properties can be used when "
+                    + "merging RRO config files: " + propertyName);
+        }
+        final String propertyValue = sysPropWrapper.get(propertyName);
+        if (TextUtils.isEmpty(propertyValue)) {
+            throw new IllegalStateException("Property is empty or doesn't exist: " + propertyName);
+        }
+        Log.d(TAG, String.format("Using property in overlay config path: \"%s\"", propertyName));
+        sb.append(propertyValue);
+
+        // propEndPos points to '}', need to step to next character, might be outside of string
+        propEndPos = propEndPos + 1;
+        // Append the remainder, if exists
+        if (propEndPos < configPath.length()) {
+            sb.append(configPath.substring(propEndPos));
+        }
+
+        return sb.toString();
+    }
+
+    /**
      * Parses a <merge> tag within an overlay configuration file.
      *
      * Merge tags allow for other configuration files to be "merged" at the current parsing
@@ -331,10 +415,21 @@
             @Nullable OverlayScanner scanner,
             @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos,
             @NonNull ParsingContext parsingContext) {
-        final String path = parser.getAttributeValue(null, "path");
+        final String path;
+
+        try {
+            SysPropWrapper sysPropWrapper = p -> {
+                return SystemProperties.get(p, "");
+            };
+            path = expandProperty(parser.getAttributeValue(null, "path"), sysPropWrapper);
+        } catch (IllegalStateException e) {
+            throw new IllegalStateException(String.format("<merge> path expand error in %s at %s",
+                    configFile, parser.getPositionDescription()), e);
+        }
+
         if (path == null) {
-            throw new IllegalStateException(String.format("<merge> without path in %s at %s"
-                    + configFile, parser.getPositionDescription()));
+            throw new IllegalStateException(String.format("<merge> without path in %s at %s",
+                    configFile, parser.getPositionDescription()));
         }
 
         if (path.startsWith("/")) {
diff --git a/core/java/com/android/internal/inputmethod/IImeTracker.aidl b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
index ab4edb6..ebae39e 100644
--- a/core/java/com/android/internal/inputmethod/IImeTracker.aidl
+++ b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
@@ -64,20 +64,28 @@
     oneway void onCancelled(in ImeTracker.Token statsToken, int phase);
 
     /**
-     * Called when the IME show request is successful.
+     * Called when the show IME request is successful.
      *
      * @param statsToken the token tracking the current IME request.
      */
     oneway void onShown(in ImeTracker.Token statsToken);
 
     /**
-     * Called when the IME hide request is successful.
+     * Called when the hide IME request is successful.
      *
      * @param statsToken the token tracking the current IME request.
      */
     oneway void onHidden(in ImeTracker.Token statsToken);
 
     /**
+     * Called when the user-controlled IME request was dispatched to the requesting app. The
+     * user animation can take an undetermined amount of time, so it shouldn't be tracked.
+     *
+     * @param statsToken the token tracking the current IME request.
+     */
+    oneway void onDispatched(in ImeTracker.Token statsToken);
+
+    /**
      * Checks whether there are any pending IME visibility requests.
      *
      * @return {@code true} iff there are pending IME visibility requests.
diff --git a/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java
index 91b80dd..24cd1c9 100644
--- a/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java
@@ -52,8 +52,14 @@
 
     ImeTracingPerfettoImpl() {
         Producer.init(InitArguments.DEFAULTS);
-        mDataSource.register(
-                new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+        DataSourceParams params =
+                new DataSourceParams.Builder()
+                        .setBufferExhaustedPolicy(
+                                PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+                        .setNoFlush(true)
+                        .setWillNotifyOnStop(false)
+                        .build();
+        mDataSource.register(params);
     }
 
 
diff --git a/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java b/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java
new file mode 100644
index 0000000..92d453b
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.annotation.BinderThread;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+/**
+ * An internal interface that mirrors {@link IInlineSuggestionsRequestCallback}.
+ *
+ * <p>This interface is used to forward incoming IPCs from
+ * {@link com.android.server.inputmethod.AutofillSuggestionsController} to
+ * {@link com.android.server.autofill.AutofillInlineSuggestionsRequestSession}.</p>
+ */
+public interface InlineSuggestionsRequestCallback {
+
+    /**
+     * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsUnsupported()}.
+     */
+    @BinderThread
+    void onInlineSuggestionsUnsupported();
+
+    /**
+     * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest(
+     * InlineSuggestionsRequest, IInlineSuggestionsResponseCallback)}.
+     */
+    @BinderThread
+    void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
+            IInlineSuggestionsResponseCallback callback);
+
+    /**
+     * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodStartInput(AutofillId)}.
+     */
+    @BinderThread
+    void onInputMethodStartInput(AutofillId imeFieldId);
+
+    /**
+     * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodShowInputRequested(boolean)}.
+     */
+    @BinderThread
+    void onInputMethodShowInputRequested(boolean requestResult);
+
+    /**
+     * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodStartInputView()}.
+     */
+    @BinderThread
+    void onInputMethodStartInputView();
+
+    /**
+     * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodFinishInputView()}.
+     */
+    @BinderThread
+    void onInputMethodFinishInputView();
+
+    /**
+     * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodFinishInput()}.
+     */
+    @BinderThread
+    void onInputMethodFinishInput();
+
+    /**
+     * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsSessionInvalidated()}.
+     */
+    @BinderThread
+    void onInlineSuggestionsSessionInvalidated();
+}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index a0aad31..2a5593f 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -297,6 +297,8 @@
                 return "SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT";
             case SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION:
                 return "SHOW_SOFT_INPUT_IMM_DEPRECATION";
+            case SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION:
+                return "CONTROL_WINDOW_INSETS_ANIMATION";
             default:
                 return "Unknown=" + reason;
         }
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index da738a0..eb6a810 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -88,6 +88,7 @@
         SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL,
         SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT,
         SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION,
+        SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION,
 })
 public @interface SoftInputShowHideReason {
     /** Default, undefined reason. */
@@ -397,4 +398,10 @@
      * {@link InputMethodManager#showSoftInputFromInputMethod(IBinder, int)}.
      */
     int SHOW_SOFT_INPUT_IMM_DEPRECATION = ImeProtoEnums.REASON_SHOW_SOFT_INPUT_IMM_DEPRECATION;
+
+    /**
+     * Show / Hide soft input by application-controlled animation in
+     * {@link android.view.InsetsController#controlWindowInsetsAnimation}.
+     */
+    int CONTROL_WINDOW_INSETS_ANIMATION = ImeProtoEnums.REASON_CONTROL_WINDOW_INSETS_ANIMATION;
 }
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 9f9aae5..24971f5 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.BatteryConsumer;
+import android.os.BatteryStats;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.PersistableBundle;
@@ -34,8 +35,12 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Container for power stats, acquired by various PowerStatsCollector classes. See subclasses for
@@ -75,6 +80,10 @@
      */
     @android.ravenwood.annotation.RavenwoodKeepWholeClass
     public static class Descriptor {
+        public static final String EXTRA_DEVICE_STATS_FORMAT = "format-device";
+        public static final String EXTRA_STATE_STATS_FORMAT = "format-state";
+        public static final String EXTRA_UID_STATS_FORMAT = "format-uid";
+
         public static final String XML_TAG_DESCRIPTOR = "descriptor";
         private static final String XML_ATTR_ID = "id";
         private static final String XML_ATTR_NAME = "name";
@@ -120,6 +129,10 @@
          */
         public final PersistableBundle extras;
 
+        private PowerStatsFormatter mDeviceStatsFormatter;
+        private PowerStatsFormatter mStateStatsFormatter;
+        private PowerStatsFormatter mUidStatsFormatter;
+
         public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId,
                 int statsArrayLength, @Nullable SparseArray<String> stateLabels,
                 int stateStatsArrayLength, int uidStatsArrayLength,
@@ -131,7 +144,7 @@
 
         public Descriptor(int customPowerComponentId, String name, int statsArrayLength,
                 @Nullable SparseArray<String> stateLabels, int stateStatsArrayLength,
-                int uidStatsArrayLength, PersistableBundle extras) {
+                int uidStatsArrayLength, @NonNull PersistableBundle extras) {
             if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) {
                 throw new IllegalArgumentException(
                         "statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH);
@@ -154,6 +167,39 @@
         }
 
         /**
+         * Returns a custom formatter for this type of power stats.
+         */
+        public PowerStatsFormatter getDeviceStatsFormatter() {
+            if (mDeviceStatsFormatter == null) {
+                mDeviceStatsFormatter = new PowerStatsFormatter(
+                        extras.getString(EXTRA_DEVICE_STATS_FORMAT));
+            }
+            return mDeviceStatsFormatter;
+        }
+
+        /**
+         * Returns a custom formatter for this type of power stats, specifically per-state stats.
+         */
+        public PowerStatsFormatter getStateStatsFormatter() {
+            if (mStateStatsFormatter == null) {
+                mStateStatsFormatter = new PowerStatsFormatter(
+                        extras.getString(EXTRA_STATE_STATS_FORMAT));
+            }
+            return mStateStatsFormatter;
+        }
+
+        /**
+         * Returns a custom formatter for this type of power stats, specifically per-UID stats.
+         */
+        public PowerStatsFormatter getUidStatsFormatter() {
+            if (mUidStatsFormatter == null) {
+                mUidStatsFormatter = new PowerStatsFormatter(
+                        extras.getString(EXTRA_UID_STATS_FORMAT));
+            }
+            return mUidStatsFormatter;
+        }
+
+        /**
          * Returns the label associated with the give state key, e.g. "5G-high" for the
          * state of Mobile Radio representing the 5G mode and high signal power.
          */
@@ -491,20 +537,22 @@
         StringBuilder sb = new StringBuilder();
         sb.append("duration=").append(durationMs).append(" ").append(descriptor.name);
         if (stats.length > 0) {
-            sb.append("=").append(Arrays.toString(stats));
+            sb.append("=").append(descriptor.getDeviceStatsFormatter().format(stats));
         }
         if (descriptor.stateStatsArrayLength != 0) {
+            PowerStatsFormatter formatter = descriptor.getStateStatsFormatter();
             for (int i = 0; i < stateStats.size(); i++) {
-                sb.append(" [");
+                sb.append(" (");
                 sb.append(descriptor.getStateLabel(stateStats.keyAt(i)));
-                sb.append("]=");
-                sb.append(Arrays.toString(stateStats.valueAt(i)));
+                sb.append(") ");
+                sb.append(formatter.format(stateStats.valueAt(i)));
             }
         }
+        PowerStatsFormatter uidStatsFormatter = descriptor.getUidStatsFormatter();
         for (int i = 0; i < uidStats.size(); i++) {
             sb.append(uidPrefix)
                     .append(UserHandle.formatUid(uidStats.keyAt(i)))
-                    .append(": ").append(Arrays.toString(uidStats.valueAt(i)));
+                    .append(": ").append(uidStatsFormatter.format(uidStats.valueAt(i)));
         }
         return sb.toString();
     }
@@ -513,26 +561,29 @@
      * Prints the contents of the stats snapshot.
      */
     public void dump(IndentingPrintWriter pw) {
-        pw.println("PowerStats: " + descriptor.name + " (" + descriptor.powerComponentId + ')');
+        pw.println(descriptor.name + " (" + descriptor.powerComponentId + ')');
         pw.increaseIndent();
         pw.print("duration", durationMs).println();
+
         if (descriptor.statsArrayLength != 0) {
-            pw.print("stats", Arrays.toString(stats)).println();
+            pw.println(descriptor.getDeviceStatsFormatter().format(stats));
         }
         if (descriptor.stateStatsArrayLength != 0) {
+            PowerStatsFormatter formatter = descriptor.getStateStatsFormatter();
             for (int i = 0; i < stateStats.size(); i++) {
-                pw.print("state ");
+                pw.print(" (");
                 pw.print(descriptor.getStateLabel(stateStats.keyAt(i)));
-                pw.print(": ");
-                pw.print(Arrays.toString(stateStats.valueAt(i)));
+                pw.print(") ");
+                pw.print(formatter.format(stateStats.valueAt(i)));
                 pw.println();
             }
         }
+        PowerStatsFormatter uidStatsFormatter = descriptor.getUidStatsFormatter();
         for (int i = 0; i < uidStats.size(); i++) {
             pw.print("UID ");
-            pw.print(uidStats.keyAt(i));
+            pw.print(UserHandle.formatUid(uidStats.keyAt(i)));
             pw.print(": ");
-            pw.print(Arrays.toString(uidStats.valueAt(i)));
+            pw.print(uidStatsFormatter.format(uidStats.valueAt(i)));
             pw.println();
         }
         pw.decreaseIndent();
@@ -542,4 +593,126 @@
     public String toString() {
         return "PowerStats: " + formatForBatteryHistory(" UID ");
     }
+
+    public static class PowerStatsFormatter {
+        private static class Section {
+            public String label;
+            public int position;
+            public int length;
+            public boolean optional;
+            public boolean typePower;
+        }
+
+        private static final double NANO_TO_MILLI_MULTIPLIER = 1.0 / 1000000.0;
+        private static final Pattern SECTION_PATTERN =
+                Pattern.compile("([^:]+):(\\d+)(\\[(?<L>\\d+)])?(?<F>\\S*)\\s*");
+        private final List<Section> mSections;
+
+        public PowerStatsFormatter(String format) {
+            mSections = parseFormat(format);
+        }
+
+        /**
+         * Produces a formatted string representing the supplied array, with labels
+         * and other adornments specific to the power stats layout.
+         */
+        public String format(long[] stats) {
+            return format(mSections, stats);
+        }
+
+        private List<Section> parseFormat(String format) {
+            if (format == null || format.isBlank()) {
+                return null;
+            }
+
+            ArrayList<Section> sections = new ArrayList<>();
+            Matcher matcher = SECTION_PATTERN.matcher(format);
+            for (int position = 0; position < format.length(); position = matcher.end()) {
+                if (!matcher.find() || matcher.start() != position) {
+                    Slog.wtf(TAG, "Bad power stats format '" + format + "'");
+                    return null;
+                }
+                Section section = new Section();
+                section.label = matcher.group(1);
+                section.position = Integer.parseUnsignedInt(matcher.group(2));
+                String length = matcher.group("L");
+                if (length != null) {
+                    section.length = Integer.parseUnsignedInt(length);
+                } else {
+                    section.length = 1;
+                }
+                String flags = matcher.group("F");
+                if (flags != null) {
+                    for (int i = 0; i < flags.length(); i++) {
+                        char flag = flags.charAt(i);
+                        switch (flag) {
+                            case '?':
+                                section.optional = true;
+                                break;
+                            case 'p':
+                                section.typePower = true;
+                                break;
+                            default:
+                                Slog.e(TAG,
+                                        "Unsupported format option '" + flag + "' in " + format);
+                                break;
+                        }
+                    }
+                }
+                sections.add(section);
+            }
+
+            return sections;
+        }
+
+        private String format(List<Section> sections, long[] stats) {
+            if (sections == null) {
+                return Arrays.toString(stats);
+            }
+
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0, count = sections.size(); i < count; i++) {
+                Section section = sections.get(i);
+                if (section.length == 0) {
+                    continue;
+                }
+
+                if (section.optional) {
+                    boolean nonZero = false;
+                    for (int offset = 0; offset < section.length; offset++) {
+                        if (stats[section.position + offset] != 0) {
+                            nonZero = true;
+                            break;
+                        }
+                    }
+                    if (!nonZero) {
+                        continue;
+                    }
+                }
+
+                if (!sb.isEmpty()) {
+                    sb.append(' ');
+                }
+                sb.append(section.label).append(": ");
+                if (section.length != 1) {
+                    sb.append('[');
+                }
+                for (int offset = 0; offset < section.length; offset++) {
+                    if (offset != 0) {
+                        sb.append(", ");
+                    }
+                    if (section.typePower) {
+                        sb.append(BatteryStats.formatCharge(
+                                stats[section.position + offset] * NANO_TO_MILLI_MULTIPLIER));
+                    } else {
+                        sb.append(stats[section.position + offset]);
+                    }
+                }
+                if (section.length != 1) {
+                    sb.append(']');
+                }
+            }
+            return sb.toString();
+        }
+    }
 }
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index ee33eb4..37b7288 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -131,8 +131,13 @@
             Runnable cacheUpdater
     ) {
         Producer.init(InitArguments.DEFAULTS);
-        mDataSource.register(new DataSourceParams(
-                DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+        DataSourceParams params =
+                new DataSourceParams.Builder()
+                        .setBufferExhaustedPolicy(
+                                DataSourceParams
+                                        .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+                        .build();
+        mDataSource.register(params);
         this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider;
         this.mViewerConfigReader = viewerConfigReader;
         this.mLogGroups = logGroups;
@@ -186,8 +191,6 @@
                 }
 
                 os.end(outProtologViewerConfigToken);
-
-                ctx.flush();
             } catch (IOException e) {
                 Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
             }
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
new file mode 100644
index 0000000..1340156
--- /dev/null
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.ravenwood;
+
+/**
+ * Class to interact with the Ravenwood environment.
+ */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+public class RavenwoodEnvironment {
+    private static RavenwoodEnvironment sInstance = new RavenwoodEnvironment();
+
+    private RavenwoodEnvironment() {
+    }
+
+    /**
+     * @return the singleton instance.
+     */
+    public static RavenwoodEnvironment getInstance() {
+        return sInstance;
+    }
+
+    /**
+     * USE IT SPARINGLY! Returns true if it's running on Ravenwood, hostside test environment.
+     *
+     * <p>Using this allows code to behave differently on a real device and on Ravenwood, but
+     * generally speaking, that's a bad idea because we want the test target code to behave
+     * differently.
+     *
+     * <p>This should be only used when different behavior is absolutely needed.
+     *
+     * <p>If someone needs it without having access to the SDK, the following hack would work too.
+     * <code>System.getProperty("java.class.path").contains("ravenwood")</code>
+     */
+    @android.ravenwood.annotation.RavenwoodReplace
+    public boolean isRunningOnRavenwood() {
+        return false;
+    }
+
+    public boolean isRunningOnRavenwood$ravenwood() {
+        return true;
+    }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f931a76..e29f256 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -47,7 +47,7 @@
     void animateExpandNotificationsPanel();
     void animateExpandSettingsPanel(String subPanel);
     void animateCollapsePanels();
-    void togglePanel();
+    void toggleNotificationsPanel();
 
     void showWirelessChargingAnimation(int batteryLevel);
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index b83b2d2..fc60f06 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -17,7 +17,6 @@
 package com.android.internal.statusbar;
 
 import android.app.Notification;
-import android.app.StatusBarManager;
 import android.content.ComponentName;
 import android.graphics.drawable.Icon;
 import android.graphics.Rect;
@@ -53,9 +52,9 @@
     void togglePanel();
     @UnsupportedAppUsage
     void disable(int what, IBinder token, String pkg);
+    void disableForUser(int what, IBinder token, String pkg, int userId);
     void disable2(int what, IBinder token, String pkg);
-    void disableForUser(in StatusBarManager.DisableInfo info, IBinder token, String pkg, int userId, String reason);
-
+    void disable2ForUser(int what, IBinder token, String pkg, int userId);
     int[] getDisableFlags(IBinder token, int userId);
     void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription);
     @UnsupportedAppUsage
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index e07acac..3d8237e 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.widget;
 
+import static android.widget.flags.Flags.messagingChildRequestLayout;
+
 import android.annotation.Nullable;
 import android.annotation.Px;
 import android.content.Context;
@@ -92,6 +94,10 @@
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             lp.hide = true;
+            // Child always needs to be measured to calculate hide property correctly in onMeasure.
+            if (messagingChildRequestLayout()) {
+                child.requestLayout();
+            }
             if (child instanceof MessagingChild) {
                 MessagingChild messagingChild = (MessagingChild) child;
                 // Whenever we encounter the message first, it's always first in the layout
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index 4031b2f..0f4615a 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -16,18 +16,27 @@
 
 package com.android.internal.widget;
 
+import android.annotation.Nullable;
 import android.app.Flags;
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
 import android.widget.RemoteViews;
 
-import androidx.annotation.Nullable;
-
 /**
  * An image view that holds the icon displayed on the left side of a notification row.
  */
 @RemoteViews.RemoteView
 public class NotificationRowIconView extends CachingIconView {
+    private boolean mApplyCircularCrop = false;
+
     public NotificationRowIconView(Context context) {
         super(context);
     }
@@ -57,4 +66,82 @@
 
         super.onFinishInflate();
     }
+
+    @Nullable
+    @Override
+    Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
+        final Drawable original = super.loadSizeRestrictedIcon(icon);
+        final Drawable result;
+        if (mApplyCircularCrop) {
+            result = makeCircularDrawable(original);
+        } else {
+            result = original;
+        }
+
+        return result;
+    }
+
+    /**
+     * Enables circle crop that makes given image circular
+     */
+    @RemotableViewMethod(asyncImpl = "setApplyCircularCropAsync")
+    public void setApplyCircularCrop(boolean applyCircularCrop) {
+        mApplyCircularCrop = applyCircularCrop;
+    }
+
+    /**
+     * Async version of {@link NotificationRowIconView#setApplyCircularCrop}
+     */
+    public Runnable setApplyCircularCropAsync(boolean applyCircularCrop) {
+        mApplyCircularCrop = applyCircularCrop;
+        return () -> {
+        };
+    }
+
+    @Nullable
+    private Drawable makeCircularDrawable(@Nullable Drawable original) {
+        if (original == null) {
+            return original;
+        }
+
+        final Bitmap source = drawableToBitmap(original);
+
+        int size = Math.min(source.getWidth(), source.getHeight());
+
+        Bitmap squared = Bitmap.createScaledBitmap(source, size, size, /* filter= */ false);
+        Bitmap result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+
+        final Canvas canvas = new Canvas(result);
+        final Paint paint = new Paint();
+        paint.setShader(
+                new BitmapShader(squared, BitmapShader.TileMode.CLAMP,
+                        BitmapShader.TileMode.CLAMP));
+        paint.setAntiAlias(true);
+        float radius = size / 2f;
+        canvas.drawCircle(radius, radius, radius, paint);
+        return new BitmapDrawable(getResources(), result);
+    }
+
+    private static Bitmap drawableToBitmap(Drawable drawable) {
+        if (drawable instanceof BitmapDrawable bitmapDrawable) {
+            final Bitmap bitmap = bitmapDrawable.getBitmap();
+            if (bitmap.getConfig() == Bitmap.Config.HARDWARE) {
+                return bitmap.copy(Bitmap.Config.ARGB_8888, false);
+            } else {
+                return bitmap;
+            }
+        }
+
+        int width = drawable.getIntrinsicWidth();
+        width = width > 0 ? width : 1;
+        int height = drawable.getIntrinsicHeight();
+        height = height > 0 ? height : 1;
+
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        drawable.draw(canvas);
+
+        return bitmap;
+    }
 }
diff --git a/core/java/com/android/internal/widget/TEST_MAPPING b/core/java/com/android/internal/widget/TEST_MAPPING
new file mode 100644
index 0000000..91cecfd
--- /dev/null
+++ b/core/java/com/android/internal/widget/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+  // v2/sysui/suite/test-mapping-sysui-screenshot-test
+  "sysui-screenshot-test": [
+    {
+      "name": "SystemUIGoogleScreenshotTests",
+      "options": [
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/jni/android_tracing_PerfettoDataSource.cpp b/core/jni/android_tracing_PerfettoDataSource.cpp
index f82ebfe..17129d8 100644
--- a/core/jni/android_tracing_PerfettoDataSource.cpp
+++ b/core/jni/android_tracing_PerfettoDataSource.cpp
@@ -244,8 +244,8 @@
     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&nativeDestroy));
 }
 
-void nativeFlush(JNIEnv* env, jclass clazz, jlong ds_ptr, jobjectArray packets) {
-    ALOG(LOG_DEBUG, LOG_TAG, "nativeFlush(%p)", (void*)ds_ptr);
+void nativeWritePackets(JNIEnv* env, jclass clazz, jlong ds_ptr, jobjectArray packets) {
+    ALOG(LOG_DEBUG, LOG_TAG, "nativeWritePackets(%p)", (void*)ds_ptr);
     sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(ds_ptr);
     datasource->WritePackets(env, packets);
 }
@@ -256,10 +256,12 @@
 }
 
 void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr,
-                              jint buffer_exhausted_policy) {
+                              jint buffer_exhausted_policy, jboolean will_notify_on_stop,
+                              jboolean no_flush) {
     sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(datasource_ptr);
 
     struct PerfettoDsParams params = PerfettoDsParamsDefault();
+    params.will_notify_on_stop = will_notify_on_stop;
     params.buffer_exhausted_policy = (PerfettoDsBufferExhaustedPolicy)buffer_exhausted_policy;
 
     params.user_arg = reinterpret_cast<void*>(datasource.get());
@@ -325,13 +327,15 @@
         datasource_instance->onStart(env);
     };
 
-    params.on_flush_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*, void* inst_ctx,
-                            struct PerfettoDsOnFlushArgs*) {
-        JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
+    if (!no_flush) {
+        params.on_flush_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*,
+                                void* inst_ctx, struct PerfettoDsOnFlushArgs*) {
+            JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
 
-        auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx);
-        datasource_instance->onFlush(env);
-    };
+            auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx);
+            datasource_instance->onFlush(env);
+        };
+    }
 
     params.on_stop_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex inst_id, void* user_arg,
                            void* inst_ctx, struct PerfettoDsOnStopArgs*) {
@@ -422,7 +426,7 @@
          (void*)nativeCreate},
         {"nativeFlushAll", "(J)V", (void*)nativeFlushAll},
         {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer},
-        {"nativeRegisterDataSource", "(JI)V", (void*)nativeRegisterDataSource},
+        {"nativeRegisterDataSource", "(JIZZ)V", (void*)nativeRegisterDataSource},
         {"nativeGetPerfettoInstanceLocked", "(JI)Landroid/tracing/perfetto/DataSourceInstance;",
          (void*)nativeGetPerfettoInstanceLocked},
         {"nativeReleasePerfettoInstanceLocked", "(JI)V",
@@ -431,11 +435,12 @@
         {"nativePerfettoDsTraceIterateBegin", "(J)Z", (void*)nativePerfettoDsTraceIterateBegin},
         {"nativePerfettoDsTraceIterateNext", "(J)Z", (void*)nativePerfettoDsTraceIterateNext},
         {"nativePerfettoDsTraceIterateBreak", "(J)V", (void*)nativePerfettoDsTraceIterateBreak},
-        {"nativeGetPerfettoDsInstanceIndex", "(J)I", (void*)nativeGetPerfettoDsInstanceIndex}};
+        {"nativeGetPerfettoDsInstanceIndex", "(J)I", (void*)nativeGetPerfettoDsInstanceIndex},
+
+        {"nativeWritePackets", "(J[[B)V", (void*)nativeWritePackets}};
 
 const JNINativeMethod gMethodsTracingContext[] = {
         /* name, signature, funcPtr */
-        {"nativeFlush", "(J[[B)V", (void*)nativeFlush},
         {"nativeGetCustomTls", "(J)Ljava/lang/Object;", (void*)nativeGetCustomTls},
         {"nativeGetIncrementalState", "(J)Ljava/lang/Object;", (void*)nativeGetIncrementalState},
         {"nativeSetCustomTls", "(JLjava/lang/Object;)V", (void*)nativeSetCustomTls},
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index bf2fdda..acef609 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -330,7 +330,7 @@
         InputDeviceInfo info = InputDeviceInfo();
         info.initialize(keyboardId, 0, 0, InputDeviceIdentifier(),
                         "keyboard " + std::to_string(keyboardId), true, false,
-                        ui::ADISPLAY_ID_DEFAULT);
+                        ui::LogicalDisplayId::DEFAULT);
         info.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
         info.setKeyCharacterMap(*charMap);
 
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c92435f..654d83c 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -607,7 +607,7 @@
 
     optional InsetsSourceProviderProto insets_source_provider = 1;
     optional WindowStateProto ime_target_from_ime = 2;
-    optional bool is_ime_layout_drawn = 3;
+    optional bool is_ime_layout_drawn = 3 [deprecated=true];
 }
 
 message BackNavigationProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 70d923b..8541704 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -299,6 +299,9 @@
 
     <protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" />
 
+    <protected-broadcast android:name="android.hardware.hdmi.action.OSD_MESSAGE" />
+    <protected-broadcast android:name="android.hardware.hdmi.action.ON_ACTIVE_SOURCE_RECOVERED_DISMISS_UI" />
+
     <protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_COMPLIANCE_CHANGED" />
diff --git a/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml
index 7c45c20..c692967 100644
--- a/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 1 bar. move to higher ground. -->
         <path
             android:name="ic_signal_cellular_1_4_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H11 V20 H6 z" />
+            android:pathData="M0,0 H11 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml
index 02b646d..b01c269 100644
--- a/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 1 bar. might have to call you back. -->
         <path
             android:name="ic_signal_cellular_1_5_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H12 V20 H6 z" />
+            android:pathData="M0,0 H12 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml
index 514d169..982623d 100644
--- a/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 2 bars. 2 out of 4 ain't bad. -->
         <path
             android:name="ic_signal_cellular_2_4_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H14 V20 H6 z" />
+            android:pathData="M0,0 H14 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml
index a97f771..75daadd 100644
--- a/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml
@@ -23,11 +23,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 2 bars. hanging in there. -->
         <path
             android:name="ic_signal_cellular_2_5_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H14 V20 H6 z" />
+            android:pathData="M0,0 H14 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml
index 1bacf4a..4e4bea3 100644
--- a/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 3 bars. quite nice. -->
         <path
             android:name="ic_signal_cellular_3_4_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H17 V20 H6 z" />
+            android:pathData="M0,0 H17 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml
index 2789d3e..9a98c29 100644
--- a/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 3 bars. not great, not terrible. -->
         <path
             android:name="ic_signal_cellular_3_5_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H16 V20 H6 z" />
+            android:pathData="M0,0 H16 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml
index 8286dbb..2a37d01 100644
--- a/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 4 bars. extremely respectable. -->
         <path
             android:name="ic_signal_cellular_4_5_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H18 V20 H6 z" />
+            android:pathData="M0,0 H18 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml b/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml
new file mode 100644
index 0000000..3b288d7
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/notification_header_height"
+    android:clipChildren="false"
+    android:tag="compactMessagingHUN"
+    android:gravity="center_vertical"
+    android:theme="@style/Theme.DeviceDefault.Notification"
+    android:importantForAccessibility="no">
+    <com.android.internal.widget.NotificationRowIconView
+        android:id="@+id/icon"
+        android:layout_width="@dimen/notification_icon_circle_size"
+        android:layout_height="@dimen/notification_icon_circle_size"
+        android:layout_gravity="center_vertical|start"
+        android:layout_marginStart="@dimen/notification_icon_circle_start"
+        android:background="@drawable/notification_icon_circle"
+        android:padding="@dimen/notification_icon_circle_padding"
+        android:maxDrawableWidth="@dimen/notification_icon_circle_size"
+        android:maxDrawableHeight="@dimen/notification_icon_circle_size"
+        />
+    <com.android.internal.widget.NotificationRowIconView
+        android:id="@+id/conversation_icon"
+        android:layout_width="@dimen/notification_icon_circle_size"
+        android:layout_height="@dimen/notification_icon_circle_size"
+        android:layout_gravity="center_vertical|start"
+        android:layout_marginStart="@dimen/notification_icon_circle_start"
+        android:maxDrawableWidth="@dimen/notification_icon_circle_size"
+        android:maxDrawableHeight="@dimen/notification_icon_circle_size"
+        android:scaleType="centerCrop"
+        android:importantForAccessibility="no"
+        />
+    <FrameLayout
+        android:id="@+id/alternate_expand_target"
+        android:layout_width="@dimen/notification_content_margin_start"
+        android:layout_height="match_parent"
+        android:layout_gravity="start"
+        android:importantForAccessibility="no"
+        android:focusable="false"
+        />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginStart="@dimen/notification_content_margin_start"
+        android:orientation="horizontal"
+        >
+        <NotificationTopLineView
+            android:id="@+id/notification_top_line"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_centerVertical="true"
+            android:layout_weight="1"
+            android:clipChildren="false"
+            android:gravity="center_vertical"
+            android:theme="@style/Theme.DeviceDefault.Notification"
+            >
+            <TextView
+                android:id="@+id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="@dimen/notification_header_separating_margin"
+                android:ellipsize="end"
+                android:fadingEdge="horizontal"
+                android:singleLine="true"
+                android:textAlignment="viewStart"
+                android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+                />
+            <include layout="@layout/notification_top_line_views" />
+        </NotificationTopLineView>
+        <FrameLayout
+            android:id="@+id/reply_action_container"
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/notification_action_list_height"
+            android:gravity="center_vertical"
+            android:orientation="horizontal" />
+        <FrameLayout
+            android:id="@+id/expand_button_touch_container"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:minWidth="@dimen/notification_content_margin_end"
+            >
+            <include layout="@layout/notification_expand_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|end"
+                />
+        </FrameLayout>
+    </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/side_fps_toast.xml b/core/res/res/layout/side_fps_toast.xml
index 2c35c9b..78299ab 100644
--- a/core/res/res/layout/side_fps_toast.xml
+++ b/core/res/res/layout/side_fps_toast.xml
@@ -25,6 +25,7 @@
         android:layout_height="wrap_content"
         android:layout_width="0dp"
         android:layout_weight="6"
+        android:paddingBottom="10dp"
         android:text="@string/fp_power_button_enrollment_title"
         android:textColor="@color/side_fps_text_color"
         android:paddingLeft="20dp"/>
@@ -36,6 +37,7 @@
         android:layout_height="wrap_content"
         android:layout_width="0dp"
         android:layout_weight="3"
+        android:paddingBottom="10dp"
         android:text="@string/fp_power_button_enrollment_button_text"
         style="?android:attr/buttonBarNegativeButtonStyle"
         android:textColor="@color/side_fps_button_color"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index b65fc5f..2a3c691 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Maak Boodskappe oop"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hoe dit werk"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Hangend …"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index cef77b0..08a290c 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"መልዕክቶች ይክፈቱ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"እንዴት እንደሚሠራ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"በመጠባበቅ ላይ..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 2a5822a..20d491f 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -165,8 +165,8 @@
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"تم الاتصال بشبكة \"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>\" المشفَّرة"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"‏أصبح الاتصال باستخدام شريحة SIM لشبكة \"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>\" أكثر أمانًا الآن"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"تم الاتصال بشبكة غير مشفَّرة"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"‏المكالمات والرسائل والبيانات هي أكثر عرضة للاختراق في الوقت الحالي أثناء استخدام شريحة SIM لشبكة \"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>\""</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"‏المكالمات والرسائل والبيانات هي أكثر عرضة للاختراق في الوقت الحالي أثناء استخدام شريحة SIM لشبكة \"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>\".\n\nستتلقّى إشعارًا آخر عندما يتم تشفير اتصالك مرة أخرى."</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"‏تكون المكالمات والرسائل والبيانات في الوقت الحالي أكثر عرضة للاختراق أثناء استخدام شريحة SIM من شبكة \"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>\""</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"‏تكون المكالمات والرسائل والبيانات في الوقت الحالي أكثر عرضة للاختراق أثناء استخدام شريحة SIM من شبكة \"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>\".\n\nستتلقّى إشعارًا آخر عندما يتم تشفير اتصالك مرة أخرى."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"إعدادات أمان شبكة الجوّال"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"مزيد من المعلومات"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"حسنًا"</string>
@@ -849,7 +849,7 @@
     <string name="policylab_forceLock" msgid="7360335502968476434">"قفل الشاشة"</string>
     <string name="policydesc_forceLock" msgid="1008844760853899693">"التحكّم في طريقة ووقت قفل الشاشة"</string>
     <string name="policylab_wipeData" msgid="1359485247727537311">"محو جميع البيانات"</string>
-    <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"يمكنك محو بيانات الجهاز اللوحي بدون تحذير، وذلك عبر إجراء إعادة الضبط على الإعدادات الأصلية."</string>
+    <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"محو بيانات الجهاز اللوحي بدون تحذير، وذلك عبر إعادة الضبط على الإعدادات الأصلية"</string>
     <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"‏يمكنك محو بيانات جهاز Android TV بدون تحذير عن طريق تنفيذ إعادة الضبط على الإعدادات الأصلية."</string>
     <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"يمكنك محو بيانات \"نظام الترفيه والمعلومات\" بدون تحذير، وذلك من خلال إعادة الضبط على الإعدادات الأصلية."</string>
     <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"محو بيانات الهاتف بدون تحذير، وذلك من خلال إعادة ضبط البيانات على الإعدادات الأصلية"</string>
@@ -2417,4 +2417,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"فتح تطبيق \"الرسائل\""</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"طريقة العمل"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"بانتظار الإزالة من الأرشيف…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 6e0da54..30ced90 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খোলক"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ই কেনেকৈ কাম কৰে"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"বিবেচনাধীন হৈ আছে..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index a3d4423..49073f1 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajı açın"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Haqqında"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gözləmədə..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index fff999a..ca66ef4 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -161,7 +161,7 @@
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Mreža u blizini je u <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> evidentirala jedinstveni ID vašeg uređaja (IMSI ili IMEI) dok ste koristili <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM.\n\nTo znači da je evidentirala vašu lokaciju, aktivnost i identitet. To je uobičajena praksa, ali može da bude problem ljudima koji su zabrinuti za privatnost."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Povezani ste na šifrovanu mrežu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Veza <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM-a je sada bezbednija"</string>
-    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Povezani ste na šifrovanu mrežu"</string>
+    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Povezani ste na nešifrovanu mrežu"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Pozivi, poruke i podaci su trenutno ranjiviji dok koristite <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Pozivi, poruke i podaci su trenutno ranjiviji dok koristite <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM.\n\nKada veza ponovo bude šifrovana, poslaćemo vam drugo obaveštenje."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Podešavanja bezbednosti na mobilnoj mreži"</string>
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Princip rada"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 19ca1c4..4cd150a 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -162,7 +162,7 @@
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"У <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> у сетцы паблізу быў запісаны ўнікальны ідэнтыфікатар вашай прылады (IMSI або IMEI) пры выкарыстанні SIM-карты <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nГэта азначае, што даныя пра ваша месцазнаходжанне, дзеянні або асобу былі зарэгістраваны. Гэта звычайная практыка, але можа быць праблемай для людзей, якія турбуюцца аб прыватнасці."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Падключана да зашыфраванай сеткі <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Цяпер падключэнне да SIM-карты <xliff:g id="NETWORK_NAME">%1$s</xliff:g> стала больш бяспечным"</string>
-    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Падключана да зашыфраванай сеткі"</string>
+    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Падключана да незашыфраванай сеткі"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Зараз выклікі, паведамленні і даныя менш абаронены пры выкарыстанні SIM-карты <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Зараз выклікі, паведамленні і даныя менш абаронены пры выкарыстанні SIM-карты <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nКалі падключэнне будзе зноў зашыфравана, вы атрымаеце апавяшчэнне."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Налады сеткавай бяспекі"</string>
@@ -2415,4 +2415,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Адкрыць Паведамленні"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як гэта працуе"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"У чаканні..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 2f1ecdb..eb2e920 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -161,7 +161,7 @@
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Установена е връзка с шифрованата мрежа <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Връзката със SIM картата от <xliff:g id="NETWORK_NAME">%1$s</xliff:g> вече е по-сигурна"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Установена е връзка с нешифрована мрежа"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Понастоящем обажданията, съобщенията и данните са по-уязвими, докато използвате SIM картата си от <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Обажданията, съобщенията и данните са по-уязвими, докато използвате SIM картата си от <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Понастоящем обажданията, съобщенията и данните са по-уязвими, докато използвате SIM картата си от <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nСлед като връзката ви бъде шифрована отново, ще получите друго известие."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Настройки за сигурност на мобилната мрежа"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Научете повече"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Отваряне на Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Начин на работа"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Изчаква..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index f1ebfb0..6618127 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -157,7 +157,7 @@
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"এনক্রিপশন, এনক্রিপটেড নয় এমন নেটওয়ার্কের জন্য বিজ্ঞপ্তি"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"ডিভাইস আইডি অ্যাক্সেস করা হয়েছে"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"আপনার <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> সিম কার্ড ব্যবহার করে <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>-এ, আশেপাশের নেটওয়ার্ক আপনার ডিভাইসের অনন্য আইডি (IMSI অথবা IMEI) রেকর্ড করেছে"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"আপনার <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> সিম কার্ড ব্যবহার করে <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>-এ, আশেপাশের নেটওয়ার্ক আপনার ডিভাইসের অনন্য আইডি (IMSI অথবা IMEI) রেকর্ড করেছে।\n\nএটির মানে হল আপনার লোকেশন, অ্যাক্টিভিটি বা পরিচিতি লগ-ইন করা হয়েছে। এটি সাধারণ পদ্ধতি কিন্তু সেইসব লোকজনের জন্য সমস্যা হতে পারে যারা নিজেদের গোপনীয়তা নিয়ে উদ্বেগে থাকেন।"</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"আপনার <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> সিম কার্ড ব্যবহার করে <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>-এ, আশেপাশের নেটওয়ার্ক আপনার ডিভাইসের অনন্য আইডি (IMSI অথবা IMEI) রেকর্ড করেছে।\n\nএটির মানে হল আপনার লোকেশন, অ্যাক্টিভিটি বা পরিচিতি লগ করা হয়েছে। এটি সাধারণ পদ্ধতি কিন্তু সেইসব লোকজনের জন্য সমস্যা হতে পারে যারা নিজেদের গোপনীয়তা নিয়ে উদ্বেগে থাকেন।"</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> এনক্রিপটেড নেটওয়ার্কের সাথে কানেক্ট করা রয়েছে"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"এখন <xliff:g id="NETWORK_NAME">%1$s</xliff:g> সিম কার্ডের কানেকশন আরও সুরক্ষিত"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"এনক্রিপটেড নয় এমন নেটওয়ার্কের সাথে কানেক্ট করা রয়েছে"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খুলুন"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"এটি কীভাবে কাজ করে"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"বাকি আছে…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 1212af9..4f058bf 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvorite Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako ovo funkcionira"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index f244779..77fd6b8 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Obre Missatges"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Com funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendent..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 6b04267..5615f79 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -2415,4 +2415,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otevřít Zprávy"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Jak to funguje"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Čeká na vyřízení…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 9bd374a..fd04e42 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Åbn Beskeder"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Sådan fungerer det"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Afventer…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 24a71b5..630ec75 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -156,12 +156,12 @@
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Sicherheit des Mobilfunknetzes"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Verschlüsselung, Benachrichtigungen für unverschlüsselte Netzwerke"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Auf Geräte-ID zugegriffen"</string>
-    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Um <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> hat ein Netzwerk in der Nähe die eindeutige ID (IMSI oder IMEI) deines Geräts aufgezeichnet, während du deine <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>-SIM verwendet hast."</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Um <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> hat ein Netzwerk in der Nähe die eindeutige ID (IMSI oder IMEI) deines Geräts aufgezeichnet, während du deine <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>-SIM verwendet hast.\n\nDaher wurden dein Standort, deine Aktivitäten oder deine Identität protokolliert. Das ist zwar üblich, kann jedoch ein Problem für Personen sein, denen ihre Privatsphäre wichtig ist."</string>
+    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Um <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> hat ein Netzwerk in der Nähe die eindeutige ID (IMSI oder IMEI) deines Geräts aufgezeichnet, während du <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>-SIM verwendet hast."</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Um <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> hat ein Netzwerk in der Nähe die eindeutige ID (IMSI oder IMEI) deines Geräts aufgezeichnet, während du <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>-SIM verwendet hast.\n\nDaher wurden dein Standort, deine Aktivitäten oder deine Identität protokolliert. Das ist zwar üblich, kann jedoch ein Problem für Personen sein, denen ihre Privatsphäre wichtig ist."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Mit verschlüsseltem Netzwerk „<xliff:g id="NETWORK_NAME">%1$s</xliff:g>“ verbunden"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Verbindung der <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-SIM ist jetzt sicherer"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Mit unverschlüsseltem Netzwerk verbunden"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Anrufe, Nachrichten und Daten sind momentan anfälliger für Angriffe, während du deine <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-SIM verwendest"</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Anrufe, Nachrichten und Daten sind anfälliger für Angriffe, während du <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-SIM verwendest"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Anrufe, Nachrichten und Daten sind momentan anfälliger für Angriffe, während du deine <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-SIM verwendest.\n\nWenn deine Verbindung wieder verschlüsselt ist, erhältst du eine weitere Benachrichtigung."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Einstellungen für die Sicherheit des Mobilfunknetzes"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Weitere Informationen"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages öffnen"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"So funktionierts"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ausstehend…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 32611ef..5490131 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Άνοιγμα Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Πώς λειτουργεί"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Σε εκκρεμότητα…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 1e1a687..a32fcca 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 9951afd..9f06f71 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -2413,4 +2413,13 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending..."</string>
+    <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
+    <string name="fingerprint_dangling_notification_msg_1" msgid="6261149111900787302">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted to improve performance"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7688302770424064884">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted to improve performance"</string>
+    <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with fingerprint."</string>
+    <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint."</string>
+    <string name="face_dangling_notification_title" msgid="947852541060975473">"Set up Face Unlock again"</string>
+    <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Your face model wasn\'t working well and was deleted. Set it up again to unlock your phone with face."</string>
+    <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string>
+    <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string>
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 8d55681..bfcc4be 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index a8e397d..8000732 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index e7f713b..0fe2ccc 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -2413,4 +2413,13 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎Open Messages‎‏‎‎‏‎"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‎‎‎‎‏‏‏‎How it works‎‏‎‎‏‎"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎Pending...‎‏‎‎‏‎"</string>
+    <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‎Set up Fingerprint Unlock again‎‏‎‎‏‎"</string>
+    <string name="fingerprint_dangling_notification_msg_1" msgid="6261149111900787302">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT">%s</xliff:g>‎‏‎‎‏‏‏‎ wasn\'t working well and was deleted to improve performance‎‏‎‎‏‎"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7688302770424064884">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎ weren\'t working well and were deleted to improve performance‎‏‎‎‏‎"</string>
+    <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT">%s</xliff:g>‎‏‎‎‏‏‏‎ wasn\'t working well and was deleted. Set it up again to unlock your phone with fingerprint.‎‏‎‎‏‎"</string>
+    <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎ weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint.‎‏‎‎‏‎"</string>
+    <string name="face_dangling_notification_title" msgid="947852541060975473">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎Set up Face Unlock again‎‏‎‎‏‎"</string>
+    <string name="face_dangling_notification_msg" msgid="8806849376915541655">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‏‏‏‎Your face model wasn\'t working well and was deleted. Set it up again to unlock your phone with face.‎‏‎‎‏‎"</string>
+    <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎Set up‎‏‎‎‏‎"</string>
+    <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‏‎Not now‎‏‎‎‏‎"</string>
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 5d4e951..8717640 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -156,7 +156,7 @@
     <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no se ha remitido"</string>
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Seguridad de redes móviles"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Encriptación, notificaciones para redes no encriptadas"</string>
-    <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Se accedió al ID de dispositivo"</string>
+    <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Se accedió al ID del dispositivo"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"A la(s) <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, una red cercana registró el ID único de tu dispositivo (IMSI o IMEI) mientras se usaba tu SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"A la(s) <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, una red cercana registró el ID único de tu dispositivo (IMSI o IMEI) mientras se usaba tu SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nEsto significa que se registró tu ubicación, actividad o identidad. Esta es una práctica común, pero podría significar un problema para personas preocupadas por su privacidad."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Se conectó a la red encriptada de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensajes"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 19be81f..6549da2 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -158,7 +158,7 @@
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Cifrado, notificaciones sobre redes no cifradas"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Se ha accedido al ID del dispositivo"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"A las <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, una red cercana registró el ID único de tu dispositivo (IMSI o IMEI) mientras usabas la SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"A las <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, una red cercana registró el ID único de tu dispositivo (IMSI o IMEI) mientras usabas la SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nEsto significa que se ha registrado tu ubicación, actividad o identidad. Se trata de una práctica habitual, pero puede ser un problema para aquellos a quienes les preocupa su privacidad."</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"A las <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, una red cercana registró el ID único de tu dispositivo (IMSI o IMEI) mientras usabas la SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nEsto significa que se ha registrado tu ubicación, actividad o identidad. Se trata de una práctica habitual, pero puede ser un problema para quienes les preocupa su privacidad."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Conectado a la red cifrada <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Ahora, la conexión con la SIM de <xliff:g id="NETWORK_NAME">%1$s</xliff:g> es más segura"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Conectado a una red no cifrada"</string>
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abre Mensajes"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 4a17e0d..92f89e3 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -154,7 +154,7 @@
     <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: pole suunatud"</string>
     <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: pole edastatud"</string>
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Mobiilsidevõrgu turve"</string>
-    <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Krüpteerimine, märguanded krüpteerimata võrkude jaoks"</string>
+    <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Krüpteerimine, märguanded krüpteerimata võrkude kohta"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Seadme ID-le on juurde pääsetud"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Kell <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> salvestas lähedal olev võrk võrgu <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM-i kasutamise ajal teie seadme kordumatu ID (IMSI või IMEI)"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Kell <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> salvestas lähedal olev võrk võrgu <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM-i kasutamise ajal teie seadme kordumatu ID (IMSI või IMEI).\n\nSee tähendab, et teie asukoht, tegevus või isik salvestati. See on levinud tava, kuid võib osutada probleemiks inimeste jaoks, kellele on privaatsus eriti oluline."</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Ava rakendus Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Tööpõhimõtted"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ootel …"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 3f3404c..2d4130e 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -2144,10 +2144,8 @@
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Ohitura moduaren informazio-jakinarazpena"</string>
     <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Bateria-aurreztailea aktibatu da"</string>
     <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Bateria-erabilera murrizten hasi da haren iraupena luzatzeko"</string>
-    <!-- no translation found for dynamic_mode_notification_title_v2 (5072385242078021152) -->
-    <skip />
-    <!-- no translation found for dynamic_mode_notification_summary_v2 (2142444344663147938) -->
-    <skip />
+    <string name="dynamic_mode_notification_title_v2" msgid="5072385242078021152">"Bateria-aurreztailea aktibatuta dago"</string>
+    <string name="dynamic_mode_notification_summary_v2" msgid="2142444344663147938">"Bateria-aurreztailea aktibatuta dago, bateriaren iraupena luzatzeko"</string>
     <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Bateria-aurreztailea"</string>
     <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Desaktibatu egin da bateria-aurreztailea"</string>
     <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Behar adina bateria dauka telefonoak. Jada ez dago eginbiderik murriztuta."</string>
@@ -2415,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Ireki Mezuak"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Nola funtzionatzen du?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Zain…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 3f850c1..07b2674 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -155,12 +155,12 @@
     <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: هدایت نشده"</string>
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"امنیت شبکه تلفن همراه"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"رمزگذاری، اعلان‌های شبکه‌های رمزگذاری‌نشده"</string>
-    <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"شناسه دستگاه دردسترس قرار گرفته است"</string>
+    <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"شناسه دستگاه  مورددسترس قرار گرفت"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"‏ساعت <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>، یکی از شبکه‌های اطراف شناسه یکتای دستگاهتان (IMSI یا IMEI) را هنگام استفاده از سیم‌کارت <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> ضبط کرده است"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"‏ساعت <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>، یکی از شبکه‌های اطراف شناسه یکتای دستگاهتان (IMSI یا IMEI) را هنگام استفاده از سیم‌کارت <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> ضبط کرده است.\n\nاین یعنی مکان، فعالیت، یا هویت شما ثبت شده است. این رویکرد عادی است اما ممکن است برای افرادی که نگران حریم خصوصی‌شان هستند مشکل‌ساز باشد."</string>
-    <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"به شبکه رمزگذاری‌شده <xliff:g id="NETWORK_NAME">%1$s</xliff:g> متصل شدید"</string>
+    <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"به شبکه رمزگذاری‌شده <xliff:g id="NETWORK_NAME">%1$s</xliff:g> متصل‌اید"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"اتصال سیم‌کارت <xliff:g id="NETWORK_NAME">%1$s</xliff:g> اکنون ایمن‌تر است"</string>
-    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"به شبکه رمزگذاری‌نشده متصل شدید"</string>
+    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"به شبکه رمزگذاری‌نشده متصل‌اید"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"درحال‌حاضر تماس‌ها، پیام‌ها، و داده‌ها هنگام استفاده از سیم‌کارت <xliff:g id="NETWORK_NAME">%1$s</xliff:g> آسیب‌پذیرتر هستند"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"درحال‌حاضر تماس‌ها، پیام‌ها، و داده‌ها هنگام استفاده از سیم‌کارت <xliff:g id="NETWORK_NAME">%1$s</xliff:g> آسیب‌پذیرتر هستند.\n\nوقتی اتصال شما دوباره رمزگذاری شود، اعلان دیگری دریافت خواهید کرد."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"تنظیمات امنیت شبکه تلفن همراه"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"باز کردن «پیام‌ها»"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"روش کار"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"درحال تعلیق…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 3e1adc4..e802443 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -160,7 +160,7 @@
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Klo <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> lähellä oleva verkko tallensi laitteesi yksilöllisen tunnuksen (IMSI tai IMEI), kun käytössä oli SIM-kortti, jonka <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> tarjoaa.\n\nTämä tarkoittaa, että sijaintisi, toimintasi tai henkilöllisyytesi on tallennettu. Tämä on tavallista mutta voi huolestuttaa ihmisiä, jotka ovat tarkkoja yksityisyydestään."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Yhdistetty salattuun verkkoon <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"SIM-yhteys, jonka <xliff:g id="NETWORK_NAME">%1$s</xliff:g> tarjoaa, on nyt turvallisempi"</string>
-    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Yhdistetty salattuun verkkoon"</string>
+    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Yhdistetty salaamattomaan verkkoon"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Puhelut, viestit ja data ovat haavoittuvaisempia, kun käytössä on SIM-kortti, jonka <xliff:g id="NETWORK_NAME">%1$s</xliff:g> tarjoaa"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Puhelut, viestit ja data ovat haavoittuvaisempia, kun käytössä on SIM-kortti, jonka <xliff:g id="NETWORK_NAME">%1$s</xliff:g> tarjoaa.\n\nKun yhteys on taas salattu, saat uuden ilmoituksen."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Mobiiliverkkoa koskevat turvallisuusasetukset"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Avaa Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Näin se toimii"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Odottaa…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index dd968e1..bf70c3c 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -162,8 +162,8 @@
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Connecté à un réseau chiffré <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"La connexion à la carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> est maintenant plus sûre"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Connecté à un réseau non chiffré"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Les appels, les messages et les données sont actuellement plus vulnérables lorsque vous utilisez votre carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Les appels, les messages et les données sont actuellement plus vulnérables lorsque vous utilisez votre carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nUne fois que votre connexion est à nouveau chiffrée, vous recevez une nouvelle notification."</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Les appels, messages et données sont plus vulnérables lorsque vous utilisez votre carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Les appels, les messages et les données sont actuellement plus vulnérables lorsque vous utilisez votre carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nLorsque votre connexion sera à nouveau chiffrée, vous recevrez une nouvelle notification."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Paramètres de sécurité du réseau cellulaire"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"En savoir plus"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"OK"</string>
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 96636dc..5727224 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -163,7 +163,7 @@
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"La connexion à la carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> est désormais plus sécurisée"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Connecté à un réseau non chiffré"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Les appels, les messages et les données sont actuellement plus vulnérables lorsque vous utilisez votre carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Les appels, les messages et les données sont actuellement plus vulnérables lorsque vous utilisez votre carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nLorsque votre connexion est à nouveau chiffrée, vous recevez une nouvelle notification."</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Les appels, les messages et les données sont actuellement plus vulnérables lorsque vous utilisez votre carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nLorsque votre connexion sera à nouveau chiffrée, vous recevrez une nouvelle notification."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Paramètres de sécurité du réseau mobile"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"En savoir plus"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"OK"</string>
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 85ad42f..663ef9a 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -158,9 +158,9 @@
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Acceso ao código do dispositivo"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Á/s <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, unha rede próxima rexistrou o código exclusivo (IMSI ou IMEI) do teu dispositivo mentres se usaba a túa SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Á/s <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, unha rede próxima rexistrou o código exclusivo (IMSI ou IMEI) do teu dispositivo mentres se usaba a túa SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nIsto significa que se rexistrou a túa localización, actividade ou identidade. Aínda que se trata dunha práctica común, pode supoñer un problema para as persoas ás que lles preocupe a súa privacidade."</string>
-    <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Conexión á rede encriptada <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Conectácheste á rede encriptada <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"A conexión coa SIM de <xliff:g id="NETWORK_NAME">%1$s</xliff:g> agora é máis segura"</string>
-    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Conexión a unha rede non encriptada"</string>
+    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Conectácheste a unha rede non encriptada"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Cando usas a SIM de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>, as chamadas, mensaxes e datos son máis vulnerables"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Cando usas a SIM de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>, as chamadas, mensaxes e datos son máis vulnerables.\n\nRecibirás outra notificación cando se volva encriptar a túa conexión."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Configuración de seguranza das redes de telefonía móbil"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensaxes"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 82ffb2f..dc42537 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ખોલો"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"તેની કામ કરવાની રીત"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"બાકી..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 8772cdc..25f6ca1 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -154,15 +154,15 @@
     <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित नहीं किया गया"</string>
     <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित नहीं किया गया"</string>
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"मोबाइल नेटवर्क की सुरक्षा"</string>
-    <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"उन नेटवर्क के लिए सुरक्षा से जुड़ी सूचनाएं जो सुरक्षित नहीं हैं"</string>
+    <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"एन्क्रिप्शन, असुरक्षित नेटवर्क के लिए सूचनाएं"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"डिवाइस आईडी को ऐक्सेस किया गया"</string>
-    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"आपके <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> सिम का इस्तेमाल करके, आस-पास के नेटवर्क ने <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> आपके डिवाइस का यूनीक आईडी (IMSI या IMEI) रिकॉर्ड किया"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"आपका <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> सिम इस्तेमाल करके, आस-पास के नेटवर्क ने <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> आपके डिवाइस का यूनीक आईडी (IMSI या IMEI) रिकॉर्ड किया.\n\nइसका मतलब है कि आपकी जगह की जानकारी, गतिविधि या निजी जानकारी को लॉग इन किया गया है. यह सामान्य तरीका है. हालांकि, यह उन लोगों के लिए समस्या की वजह हो सकता है जिन्हें अपनी निजी जानकारी को लेकर चिंता रहती है."</string>
+    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"आपके <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> सिम के ज़रिए, आस-पास के नेटवर्क ने <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> पर आपके डिवाइस का यूनीक आईडी (IMSI या IMEI) रिकॉर्ड किया"</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"आपके <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> सिम के ज़रिए, आस-पास के नेटवर्क ने <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> पर आपके डिवाइस का यूनीक आईडी (IMSI या IMEI) रिकॉर्ड किया.\n\nइसका मतलब है कि आपकी जगह की जानकारी, गतिविधि या निजी जानकारी को लॉग किया गया है. यह आम बात है. हालांकि, यह उन लोगों के लिए समस्या की वजह हो सकता है जिन्हें अपनी निजी जानकारी को लेकर चिंता रहती है."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"एन्क्रिप्ट यानी सुरक्षित नेटवर्क <xliff:g id="NETWORK_NAME">%1$s</xliff:g> से कनेक्ट किया गया"</string>
-    <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"अब <xliff:g id="NETWORK_NAME">%1$s</xliff:g> सिम का कनेक्शन ज़्यादा सुरक्षित है"</string>
+    <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"अब <xliff:g id="NETWORK_NAME">%1$s</xliff:g> सिम का कनेक्शन ज़्यादा सुरक्षित हो गया है"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"ऐसे नेटवर्क से कनेक्ट किया गया जो सुरक्षित नहीं है"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> सिम का इस्तेमाल करने के दौरान, कॉल, मैसेज, और डेटा को ऐक्सेस किए जाने का खतरा हो सकता है"</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> सिम का इस्तेमाल करने के दौरान, कॉल, मैसेज, और डेटा को ऐक्सेस किए जाने का खतरा हो सकता है.\n\nजब आपका कनेक्शन फिर से एन्क्रिप्ट यानी सुरक्षित हो जाएगा, तब आपको दोबारा सूचना भेजी जाएगी."</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> सिम का इस्तेमाल करने पर, कॉल, मैसेज, और डेटा ऐक्सेस किए जाने का खतरा हो सकता है"</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> सिम का इस्तेमाल करने पर, कॉल, मैसेज, और डेटा ऐक्सेस किए जाने का खतरा हो सकता है.\n\nजब आपका कनेक्शन फिर से एन्क्रिप्ट यानी सुरक्षित हो जाएगा, तब आपको नई सूचना भेजी जाएगी."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"मोबाइल नेटवर्क की सुरक्षा से जुड़ी सेटिंग"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"ज़्यादा जानें"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"ठीक है"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ऐप्लिकेशन खोलें"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यह सेटिंग कैसे काम करती है"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रोसेस जारी है..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 88bc29f..117d4e5 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Poruke"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako to funkcionira"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index f7e4a8f..de8fa84 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"A Messages megnyitása"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hogyan működik?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Függőben…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 251646c..d379934 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -153,9 +153,9 @@
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> վայրկյանից"</string>
     <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. Չի վերահասցեավորվել"</string>
     <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. Չի վերահասցեավորվել"</string>
-    <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Ցանցային անվտանգություն"</string>
+    <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Բջջային ցանցի անվտանգություն"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Գաղտնագրում, ծանուցումներ չգաղտնագրված ցանցերի համար"</string>
-    <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Սարքի նույնացույցիչը հասանելի է դարձել"</string>
+    <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Սարքի նույնացուցիչը հասանելի է դարձել"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Ժամը <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>-ին մոտակա ցանցը գրանցել է ձեր սարքի եզակի նույնացուցիչը (IMSI-ը կամ IMEI-ը) <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>-ի ձեր SIM քարտի օգտագործման ժամանակ"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Ժամը <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>-ին մոտակա ցանցը գրանցել է ձեր սարքի եզակի նույնացուցիչը (IMSI-ը կամ IMEI-ը) <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>-ի ձեր SIM քարտի օգտագործման ժամանակ։\n\nԴա նշանակում է, որ ձեր տեղադրությունը, գործողությունները կամ անձը նույնականացնող տվյալները գրանցվել են։ Սա սովորական գործելակերպ է, սակայն կարող է խնդիր լինել այն մարդկանց համար, որոնք մտահոգված են իրենց գաղտնիությամբ։"</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Հեռախոսը միացավ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> գաղտնագրված ցանցին"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Բացել Messages-ը"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ինչպես է դա աշխատում"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Առկախ է…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 139b4ab..e6634d2 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -157,12 +157,12 @@
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Enkripsi, notifikasi untuk jaringan yang tidak terenkripsi"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"ID perangkat diakses"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Pada <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, jaringan di sekitar merekam ID unik perangkat Anda (IMSI atau IMEI) saat menggunakan kartu SIM <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> Anda"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Pada <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, jaringan di sekitar merekam ID unik perangkat Anda (IMSI atau IMEI) saat menggunakan kartu SIM <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> Anda.\n\nHal ini berarti lokasi, aktifitas, atau identitas Anda telah dicatat dalam log. Tindakan ini adalah praktik umum tetapi dapat menjadi masalah bagi orang yang mengkhawatirkan privasi."</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Pada <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, jaringan di sekitar merekam ID unik perangkat Anda (IMSI atau IMEI) saat menggunakan kartu SIM <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> Anda.\n\nHal ini berarti lokasi, aktivitas, atau identitas Anda telah dicatat dalam log. Tindakan ini adalah praktik umum tetapi dapat menjadi masalah bagi orang yang mengkhawatirkan privasi."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Terhubung ke jaringan yang terenkripsi <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Koneksi kartu SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> kini lebih aman"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Terhubung ke jaringan yang tidak terenkripsi"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Panggilan, pesan, dan data saat ini lebih rentan saat menggunakan kartu SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> Anda"</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Panggilan, pesan, dan data saat ini lebih rentan saat menggunakan kartu SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> Anda.\n\nKetika koneksi Anda terenkripsi lagi, Anda akan menerima notifikasi lainnya."</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Panggilan, pesan, dan data saat ini lebih rentan saat menggunakan kartu SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> Anda.\n\nKetika koneksi Anda terenkripsi lagi, Anda akan kembali menerima notifikasi."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Setelan keamanan jaringan seluler"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Pelajari lebih lanjut"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"Oke"</string>
@@ -639,7 +639,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"Mengizinkan aplikasi untuk membaca lokasi dari koleksi media Anda."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"Gunakan biometrik"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gunakan biometrik atau kunci layar"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifikasi bahwa ini memang Anda"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifikasi diri Anda"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gunakan biometrik untuk melanjutkan"</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Gunakan biometrik atau kunci layar untuk melanjutkan"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometrik tidak tersedia"</string>
@@ -2221,7 +2221,7 @@
     <string name="miniresolver_call_information" msgid="6739417525304184083">"Organisasi Anda hanya mengizinkan menelepon dari aplikasi kerja"</string>
     <string name="miniresolver_sms_information" msgid="4311292661329483088">"Organisasi Anda hanya mengizinkan pengiriman pesan dari aplikasi kerja"</string>
     <string name="miniresolver_private_space_phone_information" msgid="4469511223312488570">"Anda hanya dapat melakukan panggilan telepon dari aplikasi Telepon pribadi. Panggilan yang dilakukan dengan aplikasi Telepon pribadi akan ditambahkan ke histori panggilan pribadi."</string>
-    <string name="miniresolver_private_space_messages_information" msgid="111285656327622118">"Anda hanya dapat mengirim pesan SMS dari aplikasi Message pribadi."</string>
+    <string name="miniresolver_private_space_messages_information" msgid="111285656327622118">"Anda hanya dapat mengirim pesan SMS dari aplikasi Pesan pribadi."</string>
     <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Gunakan browser pribadi"</string>
     <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Gunakan browser kerja"</string>
     <string name="miniresolver_call" msgid="6386870060423480765">"Telepon"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Buka Message"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cara kerjanya"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Tertunda..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 1f55241..f77a18a 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Opna Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Svona virkar þetta"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Í bið…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 2f066b5..95ccad5 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Apri Messaggi"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Come funziona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"In attesa…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 3fc962a..fee437a 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -162,9 +162,9 @@
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"יש חיבור לרשת המוצפנת <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"‏החיבור של כרטיס ה-SIM של <xliff:g id="NETWORK_NAME">%1$s</xliff:g> מאובטח יותר עכשיו"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"יש חיבור לרשת לא מוצפנת"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"‏השיחות, ההודעות והנתונים שלך פגיעים יותר עכשיו בזמן השימוש בכרטיס ה-SIM של <xliff:g id="NETWORK_NAME">%1$s</xliff:g> שלך"</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"‏שיחות, הודעות ונתונים פגיעים יותר עכשיו בזמן השימוש בכרטיס ה-SIM של <xliff:g id="NETWORK_NAME">%1$s</xliff:g> שלך.\n\nכשהחיבור שלך יוצפן שוב, תישלח אליך התראה נוספת."</string>
-    <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"הגדרות אבטחה של רשת סלולרית"</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"‏השיחות, ההודעות והנתונים שלך פגיעים יותר עכשיו בזמן השימוש בכרטיס ה-SIM של <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"‏השיחות, ההודעות והנתונים שלך פגיעים יותר עכשיו בזמן השימוש בכרטיס ה-SIM של <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nכשהחיבור שלך יוצפן שוב, תישלח אליך התראה נוספת."</string>
+    <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"הגדרות אבטחה של הרשת הסלולרית"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"מידע נוסף"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"הבנתי"</string>
     <string name="fcComplete" msgid="1080909484660507044">"קוד תכונה הושלם."</string>
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"‏לפתיחת Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"איך זה עובד"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"בהמתנה..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index db3ba5a..726db1c 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -154,7 +154,7 @@
     <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:転送できません"</string>
     <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:転送できません"</string>
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"モバイル ネットワーク セキュリティ"</string>
-    <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"暗号化(ネットワークが暗号化されていない場合に通知)"</string>
+    <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"暗号化、暗号化されていないネットワークに関する通知"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"デバイス ID へのアクセスが発生しました"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>、<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> の SIM の使用中に付近のネットワークでお使いのデバイスの一意の ID(IMSI または IMEI)が記録されました"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>、<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> の SIM の使用中に付近のネットワークでお使いのデバイスの一意の ID(IMSI または IMEI)が記録されました。\n\nつまり、あなたの位置情報、アクティビティ、身元などが記録されことになります。これはよくある事象ですが、プライバシーに不安を持たれている人にとっては問題になる可能性があります。"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"メッセージ アプリを開く"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"仕組み"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"保留中..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 97003c4..bdf6c48 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages-ის გახსნა"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"მუშაობის პრინციპი"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"მომლოდინე..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 116166d..55cf92b 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -162,7 +162,7 @@
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Енді <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM картасымен қосылу қорғалған."</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Шифрланбаған желіге қосылды"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM картасын пайдаланған кезде, қазіргі уақытта қоңырауларға, хабарларға және деректерге қауіп төнеді."</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM картасын пайдаланған кезде, қазіргі уақытта қоңырауларға, хабарларға және деректерге қауіп төнеді.\n\nБайланыс қайта шифрланғанда, тағы бір хабарландыру келеді."</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM картасын пайдаланған кезде, қазір қоңырауларға, хабарларға және деректерге зиян тию қаупі жоғары.\n\nБайланыс қайта шифрланғанда, тағы бір хабарландыру келеді."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Мобильдік желінің қауіпсіздік параметрлері"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Толық ақпарат"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"Түсінікті"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages қолданбасын ашу"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Бұл қалай орындалады?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Дайын емес…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 456256c..6a826db4 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -156,8 +156,8 @@
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"សុវត្ថិភាពបណ្ដាញទូរសព្ទចល័ត"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"ការ​អ៊ីនគ្រីប ការ​ជូនដំណឹងសម្រាប់បណ្ដាញដែលមិនបានអ៊ីនគ្រីប"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"បានចូលប្រើប្រាស់លេខសម្គាល់​ឧបករណ៍"</string>
-    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"នៅម៉ោង <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> បណ្ដាញនៅជិតបានកត់ត្រាលេខសម្គាល់ពិសេស (IMSI ឬ IMEI) របស់ឧបករណ៍អ្នក ពេលកំពុងប្រើស៊ីម <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> របស់អ្នក"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"នៅម៉ោង <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> បណ្ដាញនៅជិតបានកត់ត្រាលេខសម្គាល់ពិសេស (IMSI ឬ IMEI) របស់ឧបករណ៍អ្នក ពេលកំពុងប្រើស៊ីម <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> របស់អ្នក។\n\nនេះមានន័យថា ទីតាំង សកម្មភាព ឬអត្តសញ្ញាណរបស់អ្នកត្រូវបានចុះកំណត់ហេតុ។ នេះគឺជាការអនុវត្តទូទៅ ប៉ុន្តែអាចនឹងមានបញ្ហាសម្រាប់អ្នកដែលមានកង្វល់ពាក់ព័ន្ធនឹងឯកជនភាព។"</string>
+    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"នៅម៉ោង <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> បណ្ដាញនៅជិតបានកត់ត្រាលេខកូដសម្គាល់ខុសពីគេ (IMSI ឬ IMEI) របស់ឧបករណ៍អ្នក ពេលកំពុងប្រើស៊ីម <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> របស់អ្នក"</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"នៅម៉ោង <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> បណ្ដាញនៅជិតបានកត់ត្រាលេខកូដសម្គាល់ខុសពីគេ (IMSI ឬ IMEI) របស់ឧបករណ៍អ្នក ពេលកំពុងប្រើស៊ីម <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> របស់អ្នក។\n\nនេះមានន័យថា ទីតាំង សកម្មភាព ឬអត្តសញ្ញាណរបស់អ្នកត្រូវបានកត់ត្រាទុក។ នេះគឺជាការអនុវត្តទូទៅ ប៉ុន្តែអាចនឹងមានបញ្ហាសម្រាប់អ្នកដែលមានកង្វល់ពាក់ព័ន្ធនឹងឯកជនភាព។"</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"បានភ្ជាប់ទៅបណ្ដាញដែលបានអ៊ីនគ្រីប <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"ការតភ្ជាប់ស៊ីម <xliff:g id="NETWORK_NAME">%1$s</xliff:g> កាន់តែមានសុវត្ថិភាពឥឡូវនេះ"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"បានភ្ជាប់ទៅបណ្ដាញដែលមិនបានអ៊ីនគ្រីប"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"បើក​កម្មវិធី Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"របៀបដែលវាដំណើរការ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"កំពុងរង់ចាំ..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 97f0136..40d5138 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -156,12 +156,12 @@
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"ಮೊಬೈಲ್‌ ನೆಟ್‌ವರ್ಕ್‌ ಸೆಕ್ಯೂರಿಟಿ"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"ಎನ್‌ಕ್ರಿಪ್ಶನ್, ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡದ ನೆಟ್‌ವರ್ಕ್‌ಗಳಿಗೆ ನೋಟಿಫಿಕೇಶನ್‌ಗಳು"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"ಸಾಧನದ ID ಅನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲಾಗಿದೆ"</string>
-    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> ಸಮಯದಲ್ಲಿ, ನಿಮ್ಮ ಸಾಧನದ ಅನನ್ಯ ID (IMSI ಅಥವಾ IMEI) ಅನ್ನು ನಿಮ್ಮ <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM ಕಾರ್ಡ್ ಬಳಸುವಾಗ ಹತ್ತಿರದ ನೆಟ್‌ವರ್ಕ್‌ನಿಂದ ರೆಕಾರ್ಡ್ ಮಾಡಲಾಗಿದೆ"</string>
+    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>ಕ್ಕೆ, <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM ಬಳಸುವಾಗ ಸಾಧನದ ಅನನ್ಯ ID (IMSI ಅಥವಾ IMEI)ಯನ್ನು ಹತ್ತಿರದ ನೆಟ್‌ವರ್ಕ್‌ ರೆಕಾರ್ಡ್ ಮಾಡಿದೆ"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> ಸಮಯದಲ್ಲಿ, ನಿಮ್ಮ ಸಾಧನದ ಅನನ್ಯ ID (IMSI ಅಥವಾ IMEI) ಅನ್ನು ನಿಮ್ಮ <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM ಕಾರ್ಡ್ ಬಳಸುವಾಗ ಹತ್ತಿರದ ನೆಟ್‌ವರ್ಕ್‌ನಿಂದ ರೆಕಾರ್ಡ್ ಮಾಡಲಾಗಿದೆ.\n\nಇದರರ್ಥ ನಿಮ್ಮ ಸ್ಥಳ, ಚಟುವಟಿಕೆ ಅಥವಾ ಗುರುತನ್ನು ಲಾಗ್ ಮಾಡಲಾಗಿದೆ. ಇದು ಸಾಮಾನ್ಯ ವಿಧಾನವಾಗಿದೆ ಆದರೆ ಗೌಪ್ಯತೆಯ ಕುರಿತು ಕಾಳಜಿವಹಿಸುವವರಿಗೆ ಸಮಸ್ಯೆಯಾಗಬಹುದು."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಿದ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM ಕನೆಕ್ಷನ್ ಈಗ ಹೆಚ್ಚು ಸುರಕ್ಷಿತವಾಗಿದೆ"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡದ ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"ನಿಮ್ಮ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM ಕಾರ್ಡ್ ಬಳಸುವಾಗ ಕರೆಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಡೇಟಾ ಪ್ರಸ್ತುತ ಹೆಚ್ಚು ಸೂಕ್ಷ್ಮವಾಗಿರುತ್ತದೆ."</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"ನಿಮ್ಮ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM ಕಾರ್ಡ್ ಬಳಸುವಾಗ ಕರೆಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಬೇರೆಯವರು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಬಹುದಾದ ಅಪಾಯವಿರುತ್ತದೆ"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"ನಿಮ್ಮ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM ಕಾರ್ಡ್ ಬಳಸುವಾಗ ಕರೆಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಡೇಟಾ ಪ್ರಸ್ತುತ ಹೆಚ್ಚು ಸೂಕ್ಷ್ಮವಾಗಿರುತ್ತದೆ.\n\nನಿಮ್ಮ ಕನೆಕ್ಷನ್ ಅನ್ನು ಮತ್ತೆ ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಿದಾಗ, ನೀವು ಇನ್ನೊಂದು ನೋಟಿಫಿಕೇಶನ್ ಅನ್ನು ಪಡೆಯುತ್ತೀರಿ."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"ಮೊಬೈಲ್ ನೆಟ್‌ವರ್ಕ್ ಸೆಕ್ಯೂರಿಟಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ಅನ್ನು ತೆರೆಯಿರಿ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ಇದು ಹೇಗೆ ಕೆಲಸ ಮಾಡುತ್ತದೆ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ಬಾಕಿ ಉಳಿದಿದೆ..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index df29128..dd47628 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"메시지 열기"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"작동 방식"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"대기 중…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 9511db4f..bb29c36 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -156,13 +156,13 @@
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Мобилдик тармактын коопсуздугу"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Шифрлөө, шифрленбеген тармактар жөнүндө билдирмелер"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Түзмөктүн идентификатору колдонулду"</string>
-    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM картасын колдонуп жатканда жакын жердеги тармакта түзмөгүңүздүн өзгөчө идентификатору (IMSI же IMEI) жазылды"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM картасын колдонуп жатканда жакын жердеги тармакта түзмөгүңүздүн өзгөчө идентификатору (IMSI же IMEI) жазылды.\n\nЖүргөн жериңиз, аракеттериңиз же өздүгүңүз тууралуу маалымат катталды. Бул адаттагы көрүнүш болсо да, купуялыгы жөнүндө тынчсызданган адамдарга маселе жаратышы мүмкүн."</string>
-    <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Шифрленген тармакка (<xliff:g id="NETWORK_NAME">%1$s</xliff:g>) туташты"</string>
+    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Саат <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM картасы колдонулуп жатканда, жакын жердеги тармак түзмөгүңүздүн өзгөчө идентификаторун (IMSI же IMEI) жазып алды."</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Саат <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM картасы колдонулуп жатканда, жакын жердеги тармак түзмөгүңүздүн өзгөчө идентификаторун (IMSI же IMEI) жазып алды.\n\nТактап айтканда, жүргөн жериңиз, аракеттериңиз же өздүгүңүз катталды. Бул адаттагы көрүнүш болсо да, купуялыгы жөнүндө тынчсызданган адамдарга маселе жаратышы мүмкүн."</string>
+    <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Шифрленген <xliff:g id="NETWORK_NAME">%1$s</xliff:g> тармагына туташты"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Эми <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM картасы менен туташуу коопсуз болуп калды"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Шифрленбеген тармакка туташты"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Учурда <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM картасын колдонсоңуз, чалууларга, билдирүүлөргө жана маалыматтарга коркунуч жаралышы мүмкүн"</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Учурда <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM картасын колдонсоңуз, чалууларга, билдирүүлөргө жана маалыматтарга коркунуч жаралышы мүмкүн.\n\nТуташууңуз кайра шифрленгенде дагы бир билдирме аласыз."</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Учурда <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM картасын колдонсоңуз, чалууларга, билдирүүлөргө жана маалыматтарга коркунуч жаралышы мүмкүн.\n\nБайланышыңыз кайра шифрленгенде дагы бир билдирме аласыз."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Мобилдик тармактын коопсуздук параметрлери"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Кеңири маалымат"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"Түшүндүм"</string>
@@ -1334,7 +1334,7 @@
     <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
     <skip />
     <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> Интернетке туташуусу жок"</string>
-    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Параметрлерди ачуу үчүн таптап коюңуз"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Параметрлерди ачуу үчүн тийип коюңуз"</string>
     <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилдик Интернет жок"</string>
     <string name="other_networks_no_internet" msgid="6698711684200067033">"Тармактын Интернет жок"</string>
     <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Жеке DNS сервери жеткиликсиз"</string>
@@ -1396,7 +1396,7 @@
     <string name="usb_midi_notification_title" msgid="7404506788950595557">"USB аркылуу MIDI режими күйгүзүлдү"</string>
     <string name="usb_uvc_notification_title" msgid="2030032862673400008">"Түзмөк веб-камера катары туташты"</string>
     <string name="usb_accessory_notification_title" msgid="1385394660861956980">"USB шайманы туташты"</string>
-    <string name="usb_notification_message" msgid="4715163067192110676">"Кошумча параметрлерди ачуу үчүн таптап коюңуз."</string>
+    <string name="usb_notification_message" msgid="4715163067192110676">"Кошумча параметрлерди ачуу үчүн тийип коюңуз."</string>
     <string name="usb_power_notification_message" msgid="7284765627437897702">"Туташкан түзмөк кубатталууда. Дагы параметрлерди көрүү үчүн таптап коюңуз."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Аналогдук аудио жабдуу табылды"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Тиркелген түзмөк бул телефонго шайкеш келбейт. Көбүрөөк маалымат алуу үчүн таптап коюңуз."</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Жазышуулар колдонмосун ачуу"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ал кантип иштейт"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Кезекте турат..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index a0aded4..5fa0fc1 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"ເປີດ Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ມັນເຮັດວຽກແນວໃດ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ລໍຖ້າດຳເນີນການ..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 837bff7..e06c2ef 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -2415,4 +2415,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Atidaryti programą „Messages“"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kaip tai veikia"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Laukiama..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 4e24473..d2a6cd9 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Atvērt lietotni Ziņojumi"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Darbības principi"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gaida…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 3790be0..796f9efe 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -153,7 +153,7 @@
     <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> по <xliff:g id="TIME_DELAY">{2}</xliff:g> секунди"</string>
     <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не е препратено"</string>
     <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не е проследен"</string>
-    <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Обезбедување на мобилна мрежа"</string>
+    <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Безбедност на мобилна мрежа"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Шифрирање, известувања за нешифрирани мрежи"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Пристапено е до ID на уредот"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Во <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, мрежа во близина го сними уникатниот ID (IMSI или IMEI) на вашиот телефон со користење на вашата SIM-картичка на <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
@@ -161,9 +161,9 @@
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Поврзано со шифрирана мрежа <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Врската со SIM-картичката на <xliff:g id="NETWORK_NAME">%1$s</xliff:g> сега е побезбедна"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Поврзано со нешифрирана мрежа"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Повиците, пораките и податоците во моментов се почувствителни додека ја користите вашата SIM-картичка на <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Повиците, пораките и податоците се почувствителни кога го користите вашиот SIM од <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Повиците, пораките и податоците во моментов се почувствителни додека ја користите вашата SIM-картичка на <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nКога вашата врска ќе биде шифрирана повторно, ќе добиете уште едно известување."</string>
-    <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Поставки за обезбедување на мобилна мрежа"</string>
+    <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Поставки за безбедност на мобилната мрежа"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Дознајте повеќе"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"Сфатив"</string>
     <string name="fcComplete" msgid="1080909484660507044">"Кодот за карактеристиката заврши."</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Отворете ја Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Дознајте како функционира"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Во фаза на чекање…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 563efdb..a0b4c8d 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages തുറക്കുക"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ഇത് പ്രവർത്തിക്കുന്നത് എങ്ങനെയാണ്"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"തീർപ്പാക്കിയിട്ടില്ല..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index afde064..5ba16fe 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1762,7 +1762,7 @@
     <string name="user_switched" msgid="7249833311585228097">"Одоогийн хэрэглэгч <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> руу сэлгэж байна…"</string>
     <string name="user_logging_out_message" msgid="7216437629179710359">"<xliff:g id="NAME">%1$s</xliff:g>-с гарч байна…"</string>
-    <string name="owner_name" msgid="8713560351570795743">"Эзэмшигч"</string>
+    <string name="owner_name" msgid="8713560351570795743">"Өмчлөгч"</string>
     <string name="guest_name" msgid="8502103277839834324">"Зочин"</string>
     <string name="error_message_title" msgid="4082495589294631966">"Алдаа"</string>
     <string name="error_message_change_not_allowed" msgid="843159705042381454">"Энэ өөрчлөлтийг админ зөвшөөрөөгүй байна"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Мессежийг нээх"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Энэ хэрхэн ажилладаг вэ?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Хүлээгдэж буй..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 8e8d354..6c19cc5 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages उघडा"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ते कसे काम करते"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रलंबित आहे..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index fe4ab56..82c1a59 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -157,7 +157,7 @@
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Penyulitan, pemberitahuan untuk rangkaian yang tidak disulitkan"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"ID peranti diakses"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Pada <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, rangkaian berdekatan merekodkan ID unik peranti anda (IMSI atau IMEI) semasa menggunakan SIM <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> anda"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Pada <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, rangkaian berdekatan merekodkan ID unik peranti anda (IMSI atau IMEI) semasa menggunakan SIM <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> anda.\n\nHal ini bermaksud bahawa lokasi, aktiviti atau identiti anda telah dilog. Amalan ini biasa dilakukan tetapi mungkin sukar dilakukan oleh pengguna yang bimbang tentang privasi."</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Pada <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, rangkaian berdekatan merekodkan ID unik peranti anda (IMSI atau IMEI) semasa menggunakan SIM <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> anda.\n\nHal ini bermaksud bahawa lokasi, aktiviti atau identiti anda telah dilog. Amalan ini biasa dilakukan tetapi mungkin menjadi hal kepada pengguna yang bimbang tentang privasi."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Disambungkan kepada rangkaian <xliff:g id="NETWORK_NAME">%1$s</xliff:g> yang disulitkan"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Sambungan SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> kini lebih selamat"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Disambungkan kepada rangkaian yang tidak disulitkan"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Buka Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cara ciri ini berfungsi"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Belum selesai..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 5f5b0ab..43b7ffa 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -157,12 +157,12 @@
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"အသွင်ဝှက်ခြင်း၊ အသွင်ဝှက်မထားသော ကွန်ရက်များအတွက် အကြောင်းကြားချက်များ"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"စက် ID သုံးထားသည်"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> တွင် သင့် <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> ဆင်းမ်ကတ်ကို သုံးနေစဉ် အနီးရှိ ကွန်ရက်သည် စက်ပစ္စည်း၏ သီးသန့် ID (IMSI (သို့) IMEI) ကို မှတ်တမ်းတင်ထားသည်"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> တွင် သင့် <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> ဆင်းမ်ကတ်ကို သုံးနေစဉ် အနီးရှိ ကွန်ရက်သည် စက်ပစ္စည်း၏ သီးသန့် ID (IMSI (သို့) IMEI) ကို မှတ်တမ်းတင်ထားသည်။\n\nဆိုလိုသည်မှာ သင့်တည်နေရာ၊ လုပ်ဆောင်ချက် (သို့) အထောက်အထားကို မှတ်တမ်းတင်ထားသည်။ ၎င်းသည် သာမန်လုပ်ဆောင်မှုဖြစ်သော်လည်း ကိုယ်ရေးအချက်အလက်လုံခြုံမှုနှင့်ပတ်သက်၍ စိုးရိမ်သောသူများအတွက် ပြဿနာတစ်ခု ဖြစ်နိုင်သည်။"</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> တွင် သင့် <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> ဆင်းမ်ကတ်ကို သုံးနေစဉ် အနီးရှိ ကွန်ရက်သည် စက်ပစ္စည်း၏ သီးသန့် ID (IMSI (သို့) IMEI) ကို မှတ်တမ်းတင်ထားသည်။\n\nဆိုလိုသည်မှာ သင့်တည်နေရာ၊ လုပ်ဆောင်ချက် (သို့) အထောက်အထားကို မှတ်တမ်းတင်ထားသည်။ ၎င်းသည် သာမန်လုပ်ဆောင်မှုဖြစ်သော်လည်း ကိုယ်ရေးအချက်အလက်လုံခြုံမှုနှင့် ပတ်သက်၍ အချို့သူများအတွက် စိုးရိမ်စရာ ဖြစ်နိုင်သည်။"</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"အသွင်ဝှက်ထားသော ကွန်ရက် <xliff:g id="NETWORK_NAME">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားသည်"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ဆင်းမ်ကတ် ချိတ်ဆက်မှုသည် ယခု ပိုမိုလုံခြုံပါပြီ"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"အသွင်ဝှက်မထားသော ကွန်ရက်သို့ ချိတ်ဆက်ထားသည်"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"သင့် <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ဆင်းမ်ကတ်ကို သုံးနေစဉ် ဖုန်းခေါ်ဆိုမှု၊ မက်ဆေ့ဂျ်နှင့် ဒေတာများသည် လက်ရှိတွင် ပိုမိုထိခိုက်လွယ်သည်"</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"သင့် <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ဆင်းမ်ကတ်ကို သုံးနေစဉ် ဖုန်းခေါ်ဆိုမှု၊ မက်ဆေ့ဂျ်နှင့် ဒေတာများသည် လက်ရှိတွင် ပိုမိုထိခိုက်လွယ်သည်။\n\nသင့်ချိတ်ဆက်မှုကို ထပ်မံအသွင်ဝှက်လိုက်သောအခါ အကြောင်းကြားချက်နောက်တစ်ခု ရရှိပါမည်။"</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"သင့် <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ဆင်းမ် သုံးချိန်တွင် ဖုန်း၊ မက်ဆေ့ဂျ်၊ ဒေတာ လုံခြုံမှုအားနည်းသည်"</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"သင့် <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ဆင်းမ် သုံးချိန်တွင် ဖုန်း၊ မက်ဆေ့ဂျ်၊ ဒေတာ လုံခြုံမှုအားနည်းသည်။\n\nသင့်ချိတ်ဆက်မှုကို ထပ်မံအသွင်ဝှက်ပါက အကြောင်းကြားချက်နောက်တစ်ခု ရရှိပါမည်။"</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"မိုဘိုင်းကွန်ရက် လုံခြုံရေး ဆက်တင်များ"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"ပိုမိုလေ့လာရန်"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"နားလည်ပြီ"</string>
@@ -644,7 +644,7 @@
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ရှေ့ဆက်ရန် သင်၏ ဇီဝမက်ထရစ်အချက်အလက် (သို့) ဖန်သားပြင်လော့ခ်ကို သုံးပါ"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ဇီဝအချက်အလက်သုံး ကွန်ပျူတာစက်ပစ္စည်း မရရှိနိုင်ပါ"</string>
     <string name="biometric_error_user_canceled" msgid="6732303949695293730">"အထောက်အထားစိစစ်ခြင်းကို ပယ်ဖျက်လိုက်သည်"</string>
-    <string name="biometric_not_recognized" msgid="5106687642694635888">"မသိ"</string>
+    <string name="biometric_not_recognized" msgid="5106687642694635888">"မသိပါ"</string>
     <string name="biometric_face_not_recognized" msgid="5535599455744525200">"မျက်နှာကို မသိရှိပါ"</string>
     <string name="biometric_error_canceled" msgid="8266582404844179778">"အထောက်အထားစိစစ်ခြင်းကို ပယ်ဖျက်လိုက်သည်"</string>
     <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ပင်နံပါတ်၊ လော့ခ်ပုံစံ သို့မဟုတ် စကားဝှက် သတ်မှတ်မထားပါ"</string>
@@ -2220,7 +2220,7 @@
     <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"အလုပ်သုံးအက်ပ်သို့ ပြောင်းမလား။"</string>
     <string name="miniresolver_call_information" msgid="6739417525304184083">"သင့်အဖွဲ့အစည်းသည် သင့်အား အလုပ်သုံးအက်ပ်များမှသာ ဖုန်းဆက်ခွင့်ပြုသည်"</string>
     <string name="miniresolver_sms_information" msgid="4311292661329483088">"သင့်အဖွဲ့အစည်းသည် သင့်အား အလုပ်သုံးအက်ပ်များမှသာ မက်ဆေ့ဂျ်ပို့ခွင့်ပြုသည်"</string>
-    <string name="miniresolver_private_space_phone_information" msgid="4469511223312488570">"သင့်ကိုယ်ပိုင် ‘ဖုန်းအက်ပ်’ မှသာ ဖုန်းခေါ်ဆိုနိုင်သည်။ ကိုယ်ပိုင် ‘ဖုန်း’ ဖြင့် ပြုလုပ်သော ခေါ်ဆိုမှုများကို သင်၏ကိုယ်ပိုင် ခေါ်ဆိုမှုမှတ်တမ်းသို့ ထည့်ပါမည်။"</string>
+    <string name="miniresolver_private_space_phone_information" msgid="4469511223312488570">"သင့်ကိုယ်ပိုင် ‘ဖုန်းအက်ပ်’ မှသာ ဖုန်းခေါ်ဆိုနိုင်သည်။ ကိုယ်ပိုင် ‘ဖုန်း’ ဖြင့် ခေါ်ဆိုမှုများကို သင်၏ ကိုယ်ပိုင် ခေါ်ဆိုမှုမှတ်တမ်းသို့ ထည့်ပါမည်။"</string>
     <string name="miniresolver_private_space_messages_information" msgid="111285656327622118">"သင့်ကိုယ်ပိုင် Messages အက်ပ်မှသာ SMS မက်ဆေ့ဂျ်များကို ပို့နိုင်သည်။"</string>
     <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"ကိုယ်ပိုင်ဘရောင်ဇာ သုံးရန်"</string>
     <string name="miniresolver_use_work_browser" msgid="543575306251952994">"အလုပ်သုံးဘရောင်ဇာ သုံးရန်"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ဖွင့်ရန်"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"အလုပ်လုပ်ပုံ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ဆိုင်းငံ့ထားသည်…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index c4c705c..ad4ffc2 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -156,7 +156,7 @@
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Sikkerhet for mobilnettverk"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Kryptering, varsler for ukrypterte nettverk"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Enhets-ID-en er lest"</string>
-    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> registrerte et nettverk i nærheten den unike ID-en (IMSI eller IMEI) til enheten din mens du brukte SIM-kortet fra <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
+    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> registrerte et nettverk i nærheten enhetens unike ID (IMSI eller IMEI) mens du brukte <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>-SIM-kortet"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> registrerte et nettverk i nærheten den unike ID-en (IMSI eller IMEI) til enheten din mens du brukte SIM-kortet fra <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nDette betyr at posisjonen, aktiviteten eller identiteten din er registrert. Dette er en vanlig praksis, men kan være et problem for folk som er opptatt av personvern."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Koblet til det krypterte nettverket <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Tilkoblingen til SIM-kortet fra <xliff:g id="NETWORK_NAME">%1$s</xliff:g> er sikrere nå"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Åpne Meldinger"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Slik fungerer det"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Venter …"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 559f07a3..1cde247 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -156,8 +156,8 @@
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"मोबाइल नेटवर्कको सुरक्षा"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"इन्क्रिप्सन, इन्क्रिप्ट नगरिएका नेटवर्कसम्बन्धी सूचना"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"डिभाइसको ID एक्सेस गरियो"</string>
-    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"तपाईंले आफ्नो <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM प्रयोग गर्दै गर्दा तपाईंको डिभाइसको नजिकै रहेको एउटा नेटवर्कले <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> बजे तपाईंको डिभाइसको अद्वितीय ID रेकर्ड गरेको छ"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"तपाईंले आफ्नो <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM प्रयोग गर्दै गर्दा तपाईंको डिभाइसको नजिकै रहेको एउटा नेटवर्कले <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> बजे तपाईंको डिभाइसको अद्वितीय ID रेकर्ड गरेको छ।\n\nयसको अर्थ तपाईंको लोकेसन, गतिविधि वा पहिचान लग गरिएको छ। यसो गर्नु सामान्य हो तर आफ्नो गोपनीयताका बारेमा चिन्ता लिने मान्छेहरूका लागि यो समस्याको विषय हुन सक्छ।"</string>
+    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"तपाईंले आफ्नो <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM प्रयोग गर्दै गर्दा तपाईंको डिभाइसको नजिकै रहेको एउटा नेटवर्कले <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> मा तपाईंको डिभाइसको अद्वितीय ID रेकर्ड गरेको छ"</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"तपाईंले आफ्नो <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM प्रयोग गर्दै गर्दा तपाईंको डिभाइसको नजिकै रहेको एउटा नेटवर्कले <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> मा तपाईंको डिभाइसको अद्वितीय ID रेकर्ड गरेको छ।\n\nयसको अर्थ तपाईंको लोकेसन, गतिविधि वा पहिचान लग गरिएको छ। यसो गर्नु सामान्य हो तर आफ्नो गोपनीयताका बारेमा चिन्ता लिने मान्छेहरूका लागि यो समस्याको विषय हुन सक्छ।"</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"इन्क्रिप्ट गरिएको नेटवर्क <xliff:g id="NETWORK_NAME">%1$s</xliff:g> मा कनेक्ट गरियो"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM को कनेक्सन अब सुरक्षित छ"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"इन्क्रिप्ट नगरिएको नेटवर्कमा कनेक्ट गरियो"</string>
@@ -466,11 +466,11 @@
     <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"औपचारिक प्रसारणलाई पठाउनको लागि एउटा एपलाई अनुमति दिन्छ, जुन प्रसारण समाप्त भएपछि बाँकी रहन्छ। अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग गरेको कारणले ट्याब्लेटलाई ढिलो र अस्थिर बनाउन सक्छ।"</string>
     <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"एपलाई प्रसारण समाप्त भइसकेपछि पनि रहिरहने स्टिकी प्रसारणहरू पठाउने अनुमति दिन्छ। यो सुविधाको अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग हुने भएकाले तपाईंको Android टिभी यन्त्र सुस्त वा अस्थिर हुन सक्छ।"</string>
     <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"औपचारिक प्रसारणलाई पठाउनको लागि एक एपलाई अनुमति दिन्छ, जुन प्रसारण समाप्त भएपछि बाँकी रहन्छ। अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग गरेको कारणले फोनलाई ढिलो र अस्थिर बनाउन सक्छ।"</string>
-    <string name="permlab_readContacts" msgid="8776395111787429099">"तपाईँका सम्पर्कहरू पढ्नुहोस्"</string>
+    <string name="permlab_readContacts" msgid="8776395111787429099">"तपाईँका कन्ट्याक्टहरू पढ्नुहोस्"</string>
     <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"एपलाई तपाईंको ट्याब्लेटमा भण्डारण गरिएका कन्ट्याक्टहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले कन्ट्याक्टहरू बनाउने तपाईंको ट्याब्लेटमा भण्डारण गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले एपहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सेभ गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
     <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"एपलाई तपाईंको Android टिभी डिभाइसमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा पढ्न अनुमति दिन्छ। एपहरूले कन्ट्याक्टहरू बनाउने तपाईंको Android टिभी डिभाइसमा भण्डारण गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले एपहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सेभ गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
     <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"एपलाई तपाईंको फोनमा भण्डारण गरिएका कन्ट्याक्टहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले कन्ट्याक्टहरू बनाउने तपाईंको फोनमा भण्डारण गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले एपहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सेभ गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
-    <string name="permlab_writeContacts" msgid="8919430536404830430">"तपाईँका सम्पर्कहरू परिवर्तन गर्नुहोस्"</string>
+    <string name="permlab_writeContacts" msgid="8919430536404830430">"तपाईँका कन्ट्याक्टहरू परिवर्तन गर्नुहोस्"</string>
     <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"एपलाई तपाईंको ट्याब्लेटमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"</string>
     <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"एपलाई तपाईंको Android टिभी डिभाइसमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"</string>
     <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"एपलाई तपाईंको फोनमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"</string>
@@ -645,7 +645,7 @@
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"बायोमेट्रिक हार्डवेयर उपलब्ध छैन"</string>
     <string name="biometric_error_user_canceled" msgid="6732303949695293730">"प्रमाणीकरण रद्द गरियो"</string>
     <string name="biometric_not_recognized" msgid="5106687642694635888">"पहिचान भएन"</string>
-    <string name="biometric_face_not_recognized" msgid="5535599455744525200">"अनुहार पहिचान गर्न सकिएन"</string>
+    <string name="biometric_face_not_recognized" msgid="5535599455744525200">"अनुहार मिलेन"</string>
     <string name="biometric_error_canceled" msgid="8266582404844179778">"प्रमाणीकरण रद्द गरियो"</string>
     <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"कुनै पनि PIN, ढाँचा वा पासवर्ड सेट गरिएको छैन"</string>
     <string name="biometric_error_generic" msgid="6784371929985434439">"प्रमाणित गर्ने क्रममा त्रुटि भयो"</string>
@@ -666,7 +666,7 @@
   </string-array>
     <string name="fingerprint_error_not_match" msgid="4599441812893438961">"फिंगरप्रिन्ट मिलेन"</string>
     <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"फिंगरप्रिन्ट मिलेन"</string>
-    <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5590293588784953188">"अनुहार पहिचान गर्न सकिएन। बरु फिंगरप्रिन्ट प्रयोग गर्नुहोस्।"</string>
+    <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5590293588784953188">"अनुहार मिलेन। बरु फिंगरप्रिन्ट प्रयोग गर्नुहोस्।"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिन्ट प्रमाणीकरण गरियो"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"अनुहार प्रमाणीकरण गरियो"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"अनुहार प्रमाणीकरण गरियो, कृपया पुष्टि गर्नुहोस् थिच्नुहोस्"</string>
@@ -716,7 +716,7 @@
     <string name="face_acquired_not_detected" msgid="1057966913397548150">"तपाईंको अनुहार देखिएन। तपाईंको फोन आफ्नो आँखाअघि राखी समात्नुहोस्।"</string>
     <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"अत्यधिक हल्लियो। फोन स्थिर राख्नुहोस्।"</string>
     <string name="face_acquired_recalibrate" msgid="8724013080976469746">"कृपया आफ्नो अनुहार पुनः दर्ता गर्नुहोस्।"</string>
-    <string name="face_acquired_too_different" msgid="4505278456634706967">"अनुहार पहिचान गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
+    <string name="face_acquired_too_different" msgid="4505278456634706967">"अनुहार मिलेन। फेरि प्रयास गर्नुहोस्।"</string>
     <string name="face_acquired_too_similar" msgid="8882920552674125694">"आफ्नो टाउको थोरै यताउता सार्नुहोस्"</string>
     <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"आफ्नो फोनमा अझ सीधा हेर्नुहोस्"</string>
     <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"आफ्नो फोनमा अझ सीधा हेर्नुहोस्"</string>
@@ -1507,7 +1507,7 @@
     <string name="ime_action_previous" msgid="6548799326860401611">"अघिल्लो"</string>
     <string name="ime_action_default" msgid="8265027027659800121">"चलाउनुहोस्"</string>
     <string name="dial_number_using" msgid="6060769078933953531">\n"नम्बर डायल गर्नुहोस् <xliff:g id="NUMBER">%s</xliff:g> प्रयोग गरेर"</string>
-    <string name="create_contact_using" msgid="6200708808003692594">"सम्पर्क सिर्जना गर्नुहोस्\nयो <xliff:g id="NUMBER">%s</xliff:g> प्रयोग गरेर"</string>
+    <string name="create_contact_using" msgid="6200708808003692594">"कन्ट्याक्ट हाल्नुहोस्\nयो <xliff:g id="NUMBER">%s</xliff:g> प्रयोग गरेर"</string>
     <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"निम्न एउटा वा धेरै एपहरूले तपाईँको खातामा पहुँचको लागि अनुमति अहिले र भविष्यमा अनुरोध गर्छन्।"</string>
     <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"के तपाईं यस अनुरोधलाई अनुमति दिन चाहनुहुन्छ?"</string>
     <string name="grant_permissions_header_text" msgid="3420736827804657201">"अनुरोध पहुँच गर्नुहोस्"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages खोल्नुहोस्"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यसले काम गर्ने तरिका"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"विचाराधीन..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 5c393af..7bae96e 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -159,9 +159,9 @@
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Om <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> heeft een netwerk in de buurt de unieke ID van je apparaat (IMSI of IMEI) geregistreerd toen je je simkaart van <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> gebruikte"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Om <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> heeft een netwerk in de buurt de unieke ID van je apparaat (IMSI of IMEI) geregistreerd toen je je simkaart van <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> gebruikte.\n\nDit betekent dat je locatie, activiteit of identiteit is geregistreerd. Dit is gebruikelijk, maar kan een probleem zijn voor mensen die zich zorgen maken over privacy."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Verbonden met versleuteld netwerk <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
-    <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"De verbinding van de simkaart van <xliff:g id="NETWORK_NAME">%1$s</xliff:g> is nu beter beveiligd"</string>
+    <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"De verbinding van de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-sim is nu beter beveiligd"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Verbonden met een niet-versleuteld netwerk"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Gesprekken, berichten en gegevens zijn op dit moment kwetsbaarder tijdens het gebruik van je simkaart van <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Gesprekken, berichten en gegevens zijn op dit moment kwetsbaarder tijdens het gebruik van je <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-sim"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Gesprekken, berichten en gegevens zijn op dit moment kwetsbaarder tijdens het gebruik van je simkaart van <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nAls je verbinding weer versleuteld is, krijg je opnieuw een melding."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Instellingen voor beveiliging van mobiel netwerk"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Meer informatie"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Berichten openen"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hoe het werkt"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"In behandeling…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index e6fa562..c00998a 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -156,7 +156,7 @@
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"ମୋବାଇଲ ନେଟୱାର୍କ ସୁରକ୍ଷା"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"ଏନକ୍ରିପସନ, ଏନକ୍ରିପ୍ଟ କରାଯାଇନଥିବା ନେଟୱାର୍କ ପାଇଁ ବିଜ୍ଞପ୍ତି"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"ଡିଭାଇସ ID ଆକ୍ସେସ କରାଯାଇଛି"</string>
-    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"ଆପଣଙ୍କ <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> ରେକର୍ଡ କରିବା ସମୟରେ <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>ରେ ଏକ ଆଖପାଖର ନେଟୱାର୍କ ଆପଣଙ୍କ ଡିଭାଇସର ସ୍ୱତନ୍ତ୍ର ID (IMSI କିମ୍ବା IMEI) ରେକର୍ଡ କରିଛି"</string>
+    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"ଆପଣଙ୍କ <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM ବ୍ୟବହାର କରିବା ସମୟରେ <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>ରେ ଏକ ଆଖପାଖର ନେଟୱାର୍କ ଆପଣଙ୍କ ଡିଭାଇସର ସ୍ୱତନ୍ତ୍ର ID (IMSI କିମ୍ବା IMEI) ରେକର୍ଡ କରିଛି"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"ଆପଣଙ୍କ <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM ବ୍ୟବହାର କରିବା ସମୟରେ <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>ରେ ଏକ ଆଖପାଖର ନେଟୱାର୍କ ଆପଣଙ୍କ ଡିଭାଇସର ସ୍ୱତନ୍ତ୍ର ID (IMSI କିମ୍ବା IMEI) ରେକର୍ଡ କରିଛି। \n\nଏହାର ଅର୍ଥ ହେଉଛି ଯେ ଆପଣଙ୍କ ଲୋକେସନ, କାର୍ଯ୍ୟକଳାପ କିମ୍ବା ପରିଚୟକୁ ଲଗ କରାଯାଇଛି। ଏହା ସାଧାରଣ ଅଭ୍ୟାସ ଅଟେ କିନ୍ତୁ ଗୋପନୀୟତା ବିଷୟରେ ଚିନ୍ତିତ ଲୋକମାନଙ୍କ ପାଇଁ ଏହା ଏକ ସମସ୍ୟା ହୋଇପାରେ।"</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"ଏନକ୍ରିପ୍ଟ କରାଯାଇଥିବା ନେଟୱାର୍କ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ସହ କନେକ୍ଟ କରାଯାଇଛି"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM କନେକ୍ସନ ବର୍ତ୍ତମାନ ଅଧିକ ସୁରକ୍ଷିତ ଅଟେ"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ଖୋଲନ୍ତୁ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ଏହା କିପରି କାମ କରେ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ବାକି ଅଛି…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index e2586e6..77020ac 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -161,8 +161,8 @@
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"ਇਨਕ੍ਰਿਪਟਡ ਨੈੱਟਵਰਕ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> ਸਿਮ ਕਨੈਕਸ਼ਨ ਹੁਣ ਜ਼ਿਆਦਾ ਸੁਰੱਖਿਅਤ ਹੈ"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"ਇਨਕ੍ਰਿਪਟਡ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"ਫ਼ਿਲਹਾਲ ਤੁਹਾਡੇ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ਸਿਮ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਸਮੇਂ ਕਾਲਾਂ, ਸੁਨੇਹਿਆਂ ਅਤੇ ਡਾਟਾ ਜ਼ਿਆਦਾ ਖਤਰੇ ਵਿੱਚ ਹਨ"</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"ਫ਼ਿਲਹਾਲ ਤੁਹਾਡੇ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ਸਿਮ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਸਮੇਂ ਕਾਲਾਂ, ਸੁਨੇਹਿਆਂ ਅਤੇ ਡਾਟਾ ਜ਼ਿਆਦਾ ਖਤਰੇ ਵਿੱਚ ਹਨ।\n\nਤੁਹਾਡਾ ਕਨੈਕਸ਼ਨ ਦੁਬਾਰਾ ਇਨਕ੍ਰਿਪਟਡ ਹੋਣ \'ਤੇ, ਤੁਹਾਨੂੰ ਹੋਰ ਸੂਚਨਾ ਪ੍ਰਾਪਤ ਹੋਵੇਗੀ।"</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"ਫ਼ਿਲਹਾਲ ਤੁਹਾਡੇ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ਸਿਮ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਸਮੇਂ ਕਾਲਾਂ, ਸੁਨੇਹਿਆਂ ਅਤੇ ਡਾਟੇ ਤੱਕ ਪਹੁੰਚ ਕੀਤੇ ਜਾਣ ਦਾ ਖਤਰਾ ਰਹਿੰਦਾ ਹੈ"</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"ਫ਼ਿਲਹਾਲ ਤੁਹਾਡੇ <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ਸਿਮ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਸਮੇਂ ਕਾਲਾਂ, ਸੁਨੇਹਿਆਂ ਅਤੇ ਡਾਟੇ ਤੱਕ ਪਹੁੰਚ ਕੀਤੇ ਜਾਣ ਦਾ ਖਤਰਾ ਰਹਿੰਦਾ ਹੈ।\n\nਤੁਹਾਡਾ ਕਨੈਕਸ਼ਨ ਦੁਬਾਰਾ ਇਨਕ੍ਰਿਪਟਡ ਹੋਣ \'ਤੇ, ਤੁਹਾਨੂੰ ਹੋਰ ਸੂਚਨਾ ਪ੍ਰਾਪਤ ਹੋਵੇਗੀ।"</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਸੁਰੱਖਿਆ ਸੰਬੰਧੀ ਸੈਟਿੰਗਾਂ"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"ਹੋਰ ਜਾਣੋ"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"ਸਮਝ ਲਿਆ"</string>
@@ -316,7 +316,7 @@
     <string name="managed_profile_app_label" msgid="367401088383965725">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਦੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਜਾਓ"</string>
     <string name="permgrouplab_contacts" msgid="4254143639307316920">"ਸੰਪਰਕ"</string>
     <string name="permgroupdesc_contacts" msgid="9163927941244182567">"ਆਪਣੇ ਸੰਪਰਕਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ"</string>
-    <string name="permgrouplab_location" msgid="1858277002233964394">"ਟਿਕਾਣਾ"</string>
+    <string name="permgrouplab_location" msgid="1858277002233964394">"ਟਿਕਾਣੇ ਦੀ ਜਾਣਕਾਰੀ"</string>
     <string name="permgroupdesc_location" msgid="1995955142118450685">"ਇਸ ਡੀਵਾਈਸ ਦੇ ਨਿਰਧਾਰਤ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚੋ"</string>
     <string name="permgrouplab_calendar" msgid="6426860926123033230">"ਕੈਲੰਡਰ"</string>
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਤੱਕ ਪਹੁੰਚ ਕਰਨ"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ਐਪ ਖੋਲ੍ਹੋ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ਇਹ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ਵਿਚਾਰ-ਅਧੀਨ..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index d5c8d76..0971ae4 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -161,9 +161,9 @@
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"O godzinie <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> pobliska sieć zarejestrowała unikalny identyfikator Twojego urządzenia (IMSI lub IMEI) podczas korzystania z karty SIM <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"O godzinie <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> pobliska sieć zarejestrowała unikalny identyfikator Twojego urządzenia (IMSI lub IMEI) podczas korzystania z karty SIM <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nOznacza to, że zarejestrowano Twoją lokalizację, aktywność lub tożsamość. Jest to powszechna praktyka, ale może stanowić problem dla osób dbających o prywatność."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Połączono z szyfrowaną siecią <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
-    <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Połączenie SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> jest teraz bezpieczniejsze"</string>
+    <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Połączenie przy użyciu karty SIM w sieci <xliff:g id="NETWORK_NAME">%1$s</xliff:g> jest teraz bezpieczniejsze"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Połączono z niezaszyfrowaną siecią"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Połączenia, wiadomości i dane są obecnie bardziej podatne na ataki podczas korzystania z karty SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Gdy korzystasz z karty SIM w sieci <xliff:g id="NETWORK_NAME">%1$s</xliff:g>, połączenia, wiadomości i dane są bardziej narażone na ataki"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Połączenia, wiadomości i dane są obecnie bardziej podatne na ataki podczas korzystania z karty SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nGdy połączenie zostanie ponownie zaszyfrowane, otrzymasz kolejne powiadomienie."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Ustawienia bezpieczeństwa sieci komórkowej"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Więcej informacji"</string>
@@ -2415,4 +2415,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otwórz Wiadomości"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Jak to działa"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Oczekiwanie…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 4d9e208..b04cd4c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -164,7 +164,7 @@
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Conectado à rede não criptografada"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Suas ligações, mensagens e dados estão mais vulneráveis no momento ao usar seu chip da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Ligações, mensagens e dados estão mais vulneráveis no momento ao usar seu chip da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nQuando a conexão for criptografada novamente, você vai receber outra notificação."</string>
-    <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Configurações de segurança de rede móvel"</string>
+    <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Configurações de segurança da rede móvel"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Saiba mais"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"Entendi"</string>
     <string name="fcComplete" msgid="1080909484660507044">"Código de recurso concluído."</string>
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir o app Mensagens"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 2074d1e..90f9eda 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1389,7 +1389,7 @@
     <string name="no_permissions" msgid="5729199278862516390">"Não são necessárias permissões"</string>
     <string name="perm_costs_money" msgid="749054595022779685">"isto poderá estar sujeito a custos"</string>
     <string name="dlg_ok" msgid="5103447663504839312">"OK"</string>
-    <string name="usb_charging_notification_title" msgid="1674124518282666955">"O dispositivo está a carregar por USB"</string>
+    <string name="usb_charging_notification_title" msgid="1674124518282666955">"O dispositivo está a ser carregado por USB"</string>
     <string name="usb_supplying_notification_title" msgid="5378546632408101811">"A carregar o dispositivo ligado por USB"</string>
     <string name="usb_mtp_notification_title" msgid="1065989144124499810">"A transferência de ficheiros por USB está ativada"</string>
     <string name="usb_ptp_notification_title" msgid="5043437571863443281">"O PTP por USB está ativado"</string>
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abre a app Mensagens"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 4d9e208..b04cd4c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -164,7 +164,7 @@
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Conectado à rede não criptografada"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Suas ligações, mensagens e dados estão mais vulneráveis no momento ao usar seu chip da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Ligações, mensagens e dados estão mais vulneráveis no momento ao usar seu chip da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nQuando a conexão for criptografada novamente, você vai receber outra notificação."</string>
-    <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Configurações de segurança de rede móvel"</string>
+    <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Configurações de segurança da rede móvel"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Saiba mais"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"Entendi"</string>
     <string name="fcComplete" msgid="1080909484660507044">"Código de recurso concluído."</string>
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir o app Mensagens"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 10aa020..c43df4f 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Deschide Mesaje"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cum funcționează"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"În așteptare..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index ff0d1b8..32a2338 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -158,11 +158,11 @@
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Безопасность мобильной сети"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Шифрование, уведомления о незашифрованных сетях"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Получен доступ к идентификатору устройства"</string>
-    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"В <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> при использовании SIM-карты (<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>) в сети поблизости был записан уникальный идентификатор вашего устройства (IMSI или IMEI)."</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"В <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> при использовании SIM-карты (<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>) в сети поблизости был записан уникальный идентификатор вашего устройства (IMSI или IMEI).\n\nВаше местоположение, действия или личность были зарегистрированы. Хотя в этом нет ничего необычного, раскрытие данных может доставлять проблемы людям, которые беспокоятся о своей конфиденциальности."</string>
-    <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Подключение к зашифрованной сети \"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>\""</string>
-    <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Теперь подключение SIM-карты (<xliff:g id="NETWORK_NAME">%1$s</xliff:g>) более безопасное."</string>
-    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Подключение к незашифрованной сети"</string>
+    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"В <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, когда вы использовали SIM-карту \"<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>\", сетью поблизости от вас был записан уникальный идентификатор вашего устройства (IMSI или IMEI)."</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"В <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, когда вы использовали SIM-карту \"<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>\", сетью поблизости от вас был записан уникальный идентификатор вашего устройства (IMSI или IMEI).\n\nЭто значит, что было зарегистрировано ваше местоположение, действия или личность. Это распространенная практика, однако она может вызывать беспокойство у тех, кто заботится о своей конфиденциальности."</string>
+    <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Подключено к зашифрованной сети <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Теперь подключение SIM-карты (<xliff:g id="NETWORK_NAME">%1$s</xliff:g>) лучше защищено."</string>
+    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Подключено к незашифрованной сети"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Сейчас звонки, сообщения и данные более уязвимы при использовании SIM-карты (<xliff:g id="NETWORK_NAME">%1$s</xliff:g>)."</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Сейчас звонки, сообщения и данные более уязвимы при использовании SIM-карты (<xliff:g id="NETWORK_NAME">%1$s</xliff:g>).\n\nКогда соединение будет снова зашифровано, вы получите ещё одно уведомление."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Настройки безопасности мобильной сети"</string>
@@ -2415,4 +2415,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Открыть Сообщения"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Узнать принцип работы"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Обработка…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 18b26f2..9539cef 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages විවෘත කරන්න"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"එය ක්‍රියා කරන ආකාරය"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"පොරොත්තුයි..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 3b144d2..c72a426d 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -163,8 +163,8 @@
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Pripojené k šifrovanej sieti <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Pripojenie SIM siete <xliff:g id="NETWORK_NAME">%1$s</xliff:g> je teraz zabezpečenejšie"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Pripojené k nešifrovanej sieti"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Hovory, správy a údaje sú momentálne zraniteľnejšie vzhľadom na nedostatok zabezpečenia pri používaní SIM siete <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Hovory, správy a údaje sú momentálne zraniteľnejšie vzhľadom na nedostatok zabezpečenia pri používaní SIM siete <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nKeď bude vaše pripojenie znova šifrované, dostanete ďalšie upozornenie."</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Pri používaní SIM siete <xliff:g id="NETWORK_NAME">%1$s</xliff:g> sú vaše hovory, správy a údaje zraniteľnejšie"</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Pri používaní SIM siete <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nsú vaše hovory, správy a údaje zraniteľnejšie. Keď bude vaše pripojenie znova šifrované, dostanete ďalšie upozornenie."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Nastavenia zabezpečenia mobilnej siete"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Ďalšie informácie"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"Dobre"</string>
@@ -2415,4 +2415,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvoriť Správy"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ako to funguje"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Nespracovaná…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 1b7a2a1..e1be803 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -2415,4 +2415,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Odpri Sporočila"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako deluje"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"V teku …"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 142ab0d..4d79ce7 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -157,12 +157,12 @@
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Enkriptimi, njoftimet për rrjetet e paenkriptuara"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Pati qasje tek ID-ja e pajisjes"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Në <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, një rrjet në afërsi ka regjistruar ID-në unike (IMSI ose IMEI) të pajisjes sate gjatë përdorimit të kartës sate SIM të <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Në <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, një rrjet në afërsi ka regjistruar ID-në unike (IMSI ose IMEI) të pajisjes sate gjatë përdorimit të kartës sate SIM të <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nKjo do të thotë që vendndodhja, aktiviteti ose identiteti yt janë regjistruar. Kjo është një praktikë të zakonshme, por mund të jetë problem për personat që janë të shqetësuar në lidhje me privatësinë."</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Në <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, një rrjet në afërsi ka regjistruar ID-në unike (IMSI ose IMEI) të pajisjes sate gjatë përdorimit të kartës sate SIM të <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nKjo do të thotë që vendndodhja, aktiviteti ose identiteti yt janë regjistruar. Kjo është një praktikë e zakonshme, por mund të jetë problem për personat që janë të shqetësuar në lidhje me privatësinë."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Lidhur me rrjetin e enkriptuar <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Lidhja e kartës SIM të <xliff:g id="NETWORK_NAME">%1$s</xliff:g> është më e sigurt tani"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Lidhur me një rrjet të paenkriptuar"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Telefonatat, mesazhet dhe të dhënat janë aktualisht më të cenueshme ndërkohë që përdoret karta jote SIM të <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Telefonatat, mesazhet dhe të dhënat janë aktualisht më të cenueshme ndërkohë që përdoret karta jote SIM të <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nKur lidhja jote të jetë përsëri e enkriptuar, do të marrësh një njoftim tjetër."</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Telefonatat, mesazhet dhe të dhënat janë aktualisht më të cenueshme teksa përdoret karta SIM e <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Telefonatat, mesazhet dhe të dhënat janë aktualisht më të cenueshme teksa përdoret karta SIM e <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nKur lidhja jote të jetë përsëri e enkriptuar, do të marrësh një njoftim tjetër."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Cilësimet e sigurisë së rrjetit celular"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Mëso më shumë"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"E kuptova"</string>
@@ -2144,10 +2144,8 @@
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Njoftimi i informacionit të \"Modalitetit rutinë\""</string>
     <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"\"Kursyesi i baterisë\" u aktivizua"</string>
     <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Po reduktohet përdorimi i baterisë për të rritur kohëzgjatjen e baterisë"</string>
-    <!-- no translation found for dynamic_mode_notification_title_v2 (5072385242078021152) -->
-    <skip />
-    <!-- no translation found for dynamic_mode_notification_summary_v2 (2142444344663147938) -->
-    <skip />
+    <string name="dynamic_mode_notification_title_v2" msgid="5072385242078021152">"\"Kursyesi i baterisë\" është aktiv"</string>
+    <string name="dynamic_mode_notification_summary_v2" msgid="2142444344663147938">"\"Kursyesi i baterisë\" është aktivizuar për të zgjatur kohëzgjatjen e baterisë"</string>
     <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Kursyesi i baterisë"</string>
     <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"\"Kursyesi i baterisë\" është çaktivizuar"</string>
     <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Telefoni ka nivel të mjaftueshëm baterie. Funksionet nuk janë më të kufizuara."</string>
@@ -2415,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Hap \"Mesazhet\""</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Si funksionon"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Në pritje..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 87d1086..95ccead 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -161,7 +161,7 @@
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Мрежа у близини је у <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> евидентирала јединствени ИД вашег уређаја (IMSI или IMEI) док сте користили <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM.\n\nТо значи да је евидентирала вашу локацију, активност и идентитет. То је уобичајена пракса, али може да буде проблем људима који су забринути за приватност."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Повезани сте на шифровану мрежу <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Веза <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM-а је сада безбеднија"</string>
-    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Повезани сте на шифровану мрежу"</string>
+    <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Повезани сте на нешифровану мрежу"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Позиви, поруке и подаци су тренутно рањивији док користите <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Позиви, поруке и подаци су тренутно рањивији док користите <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM.\n\nКада веза поново буде шифрована, послаћемо вам друго обавештење."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Подешавања безбедности на мобилној мрежи"</string>
@@ -2414,4 +2414,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Отвори Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Принцип рада"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"На чекању..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 3ca5c9f..0cbdda1 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Öppna Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Så fungerar det"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Väntar …"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 2efca2b..10f55af 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -161,8 +161,8 @@
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Imeunganishwa kwenye mtandao uliosimbwa kwa njia fiche wa <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Sasa muunganisho wa SIM wa <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ni salama zaidi"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Imeunganishwa kwenye mtandao ambao haujasimbwa kwa njia fiche"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Kwa sasa maudhui ya simu, ujumbe na data yako katika hatari ya kuvamiwa unapotumia SIM yako ya <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
-    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Kwa sasa maudhui ya simu, ujumbe na data yako katika hatari ya kuvamiwa unapotumia SIM yako ya <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nMuunganisho wako ukisimbwa kwa njia fiche tena, utapata arifa nyingine."</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Kwa sasa maudhui ya simu, ujumbe na data ipo katika hatari ya kuvamiwa unapotumia SIM yako ya <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Kwa sasa maudhui ya simu, ujumbe na data ipo katika hatari ya kuvamiwa unapotumia SIM yako ya <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nMuunganisho wako ukisimbwa kwa njia fiche tena, utapata arifa nyingine."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Mipangilio ya usalama wa mtandao wa simu"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Pata maelezo zaidi"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"Nimeelewa"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Fungua Programu ya Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Utaratibu wake"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Inashughulikiwa..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 0c704e3..466e29a 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ஆப்ஸைத் திறக்கவும்"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"இது செயல்படும் விதம்"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"நிலுவையிலுள்ளது..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index b465000..db75aac 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -156,7 +156,7 @@
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"మొబైల్ నెట్‌వర్క్ సెక్యూరిటీ"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"ఎన్‌క్రిప్షన్, ఎన్‌క్రిప్ట్ చేయని నెట్‌వర్క్‌లకు సంబంధించిన నోటిఫికేషన్‌లు"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"పరికర IDని యాక్సెస్ చేశారు"</string>
-    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"మీ <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIMను ఉపయోగిస్తున్నప్పుడు, సమీపంలోని నెట్‌వర్క్ <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> సమయానికి మీ పరికర యూనిక్ ID (IMSI లేదా IMEI)ని రికార్డ్ చేసింది"</string>
+    <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"మీ <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIMను వాడేటప్పుడు, <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>కి దగ్గర్లోని ఒక నెట్‌వర్క్, మీ పరికర యూనిక్ IDని (IMSI లేదా IMEI) రికార్డ్ చేసింది"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"మీ <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIMను ఉపయోగిస్తున్నప్పుడు, సమీపంలోని నెట్‌వర్క్ <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> సమయానికి మీ పరికర యూనిక్ ID (IMSI లేదా IMEI)ని రికార్డ్ చేసింది.\n\nమీ లొకేషన్, యాక్టివిటీ, లేదా గుర్తింపు లాగ్ అయ్యాయని దీని అర్థం. దీనిని తరచుగా అమలు చేస్తుంటారు, కానీ గోప్యత గురించి ఆందోళనపడే వ్యక్తులకు ఇది సమస్య కావచ్చు,"</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"ఎన్‌క్రిప్ట్ చేసిన నెట్‌వర్క్ <xliff:g id="NETWORK_NAME">%1$s</xliff:g>‌కు కనెక్ట్ అయింది"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM కనెక్షన్ ఇప్పుడు మరింత సురక్షితంగా ఉంది"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messagesను తెరవండి"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ఇది ఎలా పని చేస్తుంది"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"పెండింగ్‌లో ఉంది..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 535396d..284a5f2 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1619,7 +1619,7 @@
     <string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"เกินปริมาณเน็ตมือถือที่กำหนดไว้"</string>
     <string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"เกินขีดจำกัดของข้อมูล Wi-Fi"</string>
     <string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"คุณใช้อินเทอร์เน็ตเกินไป <xliff:g id="SIZE">%s</xliff:g> จากปริมาณที่กำหนดไว้"</string>
-    <string name="data_usage_restricted_title" msgid="126711424380051268">"จำกัดอินเทอร์เน็ตที่ใช้งานอยู่เบื้องหลัง"</string>
+    <string name="data_usage_restricted_title" msgid="126711424380051268">"จำกัดข้อมูลในเบื้องหลัง"</string>
     <string name="data_usage_restricted_body" msgid="5338694433686077733">"แตะเพื่อนำข้อจำกัดออก"</string>
     <string name="data_usage_rapid_title" msgid="2950192123248740375">"ปริมาณการใช้เน็ตมือถือสูง"</string>
     <string name="data_usage_rapid_body" msgid="3886676853263693432">"แอปของคุณใช้อินเทอร์เน็ตมากกว่าปกติ"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"เปิด Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"วิธีการทำงาน"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"รอดำเนินการ..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 6eab396..4dd4886 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -161,7 +161,7 @@
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Nakakonekta sa naka-encrypt na network na <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Mas secure na ang koneksyon ng <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Nakakonekta sa hindi naka-encrypt na network"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Kasalukuyang mas nanganganib ang mga tawag, mensahe, at data habang ginagamit ang iyong <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM"</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Kasalukuyang walang proteksyon ang tawag, mensahe, at data habang gamit ang <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM mo"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Kasalukuyang mas nanganganib ang mga tawag, mensahe, at data habang ginagamit ang iyong <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM.\n\nKapag na-encrypt ulit ang iyong koneksyon, makakatanggap ka ng isa pang notification."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Mga setting ng seguridad ng mobile network"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Matuto pa"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Buksan ang Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Paano ito gumagana"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Nakabinbin..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 4c8083a..2b616de 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajlar\'ı aç"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"İşleyiş şekli"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Bekliyor..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 6f94a2c..850e21b 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -2146,10 +2146,8 @@
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Сповіщення про програму"</string>
     <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Режим енергозбереження ввімкнено"</string>
     <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Заряд використовується економно, щоб подовжити час роботи акумулятора"</string>
-    <!-- no translation found for dynamic_mode_notification_title_v2 (5072385242078021152) -->
-    <skip />
-    <!-- no translation found for dynamic_mode_notification_summary_v2 (2142444344663147938) -->
-    <skip />
+    <string name="dynamic_mode_notification_title_v2" msgid="5072385242078021152">"Режим енергозбереження ввімкнено"</string>
+    <string name="dynamic_mode_notification_summary_v2" msgid="2142444344663147938">"Увімкнено режим енергозбереження, щоб збільшити час роботи акумулятора"</string>
     <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Режим енергозбереження"</string>
     <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Режим енергозбереження вимкнено."</string>
     <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Телефон має достатньо заряду акумулятора. Функції вже не обмежено."</string>
@@ -2417,4 +2415,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Відкрийте Повідомлення"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як це працює"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Обробка…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index ed1a358..d3fde3c 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -157,7 +157,7 @@
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"غیر مرموز کردہ نیٹ ورکس کے لیے مرموز کاری، اطلاعات"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"‏آلہ ID تک رسائی کی گئی"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"‏<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> میں، قریبی نیٹ ورک نے آپ کے <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM کا استعمال کرتے ہوئے آپ کے آلے کی منفرد ID (IMSI یا IMEI) ریکارڈ کی"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"‏<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> میں، آپ کے <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM استعمال کرتے ہوئے قریبی نیٹ ورک نے آپ کے آلے کی منفرد ID (IMSI یا IMEI) کو ریکارڈ کیا۔\n\nاس کا مطلب ہے کہ آپ کا مقام، سرگرمی یا شناخت لاگ ان ہو چکی ہیں۔ یہ عام بات ہے لیکن رازداری کے بارے میں فکر مند لوگوں کے لیے ایک مسئلہ ہو سکتا ہے۔"</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"‏<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> میں، آپ کے <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM استعمال کرتے ہوئے قریبی نیٹ ورک نے آپ کے آلے کی منفرد ID (IMSI یا IMEI) کو ریکارڈ کیا۔\n\nاس کا مطلب ہے کہ آپ کا مقام، سرگرمی یا شناخت کو ریکارڈ کیا جا چکا ہے۔ یہ عام بات ہے لیکن رازداری کے بارے میں فکر مند لوگوں کے لیے ایک مسئلہ ہو سکتا ہے۔"</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"مرموز کردہ نیٹ ورک <xliff:g id="NETWORK_NAME">%1$s</xliff:g> سے منسلک ہے"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"‏‫<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM کا کنکشن اب بہت محفوظ ہے"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"غیر مرموز کردہ نیٹ ورک سے منسلک ہے"</string>
@@ -644,7 +644,7 @@
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"جاری رکھنے کے لیے اپنے بایو میٹرک اور اسکرین لاک کا استعمال کریں"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"بایومیٹرک ہارڈ ویئر دستیاب نہیں ہے"</string>
     <string name="biometric_error_user_canceled" msgid="6732303949695293730">"تصدیق کا عمل منسوخ ہو گیا"</string>
-    <string name="biometric_not_recognized" msgid="5106687642694635888">"تسلیم شدہ نہیں ہے"</string>
+    <string name="biometric_not_recognized" msgid="5106687642694635888">"شناخت نہیں ہو سکی"</string>
     <string name="biometric_face_not_recognized" msgid="5535599455744525200">"چہرے کی شناخت نہیں ہو سکی"</string>
     <string name="biometric_error_canceled" msgid="8266582404844179778">"تصدیق کا عمل منسوخ ہو گیا"</string>
     <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"کوئی پن، پیٹرن، یا پاس ورڈ سیٹ نہیں ہے"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"پیغامات ایپ کو کھولیں"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"اس کے کام کرنے کا طریقہ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"زیر التواء..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 4ddd368..c143244 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Xabarlar ilovasini ochish"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ishlash tartibi"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Kutilmoqda..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index c633c2a..a0775d4 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -155,15 +155,15 @@
     <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Không được chuyển tiếp"</string>
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Bảo mật mạng di động"</string>
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Mã hoá, thông báo cho mạng chưa được mã hoá"</string>
-    <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Mã thiết bị được truy cập"</string>
+    <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Mã thiết bị đã bị truy cập"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Vào <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, một mạng lân cận đã ghi nhận mã nhận dạng duy nhất của thiết bị của bạn (IMSI hoặc IMEI) trong lúc sử dụng SIM <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> của bạn"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Vào <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, một mạng lân cận đã ghi nhận mã nhận dạng duy nhất của thiết bị của bạn (IMSI hoặc IMEI) trong lúc sử dụng SIM <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> của bạn.\n\nTức là vị trí, hoạt động hoặc danh tính của bạn đã được ghi lại. Đây là một phương thức thông dụng nhưng có thể trở thành vấn đề đối với những ai lo ngại về quyền riêng tư."</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Vào <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, một mạng lân cận đã ghi lại mã nhận dạng duy nhất của thiết bị của bạn (IMSI hoặc IMEI) trong lúc sử dụng SIM <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> của bạn.\n\nTức là vị trí, hoạt động hoặc danh tính của bạn đã được ghi lại. Đây là một phương thức thông dụng nhưng có thể trở thành vấn đề đối với những ai lo ngại về quyền riêng tư."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Đã kết nối với mạng <xliff:g id="NETWORK_NAME">%1$s</xliff:g> đã mã hoá"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Kết nối SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> nay an toàn hơn"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Đã kết nối với mạng chưa được mã hoá"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Cuộc gọi, tin nhắn và dữ liệu hiện dễ bị tấn công trong lúc sử dụng SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> của bạn."</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Cuộc gọi, tin nhắn và dữ liệu hiện dễ bị tấn công trong lúc sử dụng SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> của bạn.\n\nKhi kết nối của bạn được mã hoá lại, bạn sẽ nhận được một thông báo khác."</string>
-    <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Chế độ bảo mật mạng di động"</string>
+    <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Cài đặt an ninh mạng di động"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Tìm hiểu thêm"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"Tôi hiểu"</string>
     <string name="fcComplete" msgid="1080909484660507044">"Mã tính năng đã hoàn tất."</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Mở ứng dụng Tin nhắn"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cách hoạt động"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Đang chờ xử lý..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 31acd9a..5266214 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -93,4 +93,7 @@
 
     <!-- If this is true, allow wake from theater mode from motion. -->
     <bool name="config_allowTheaterModeWakeFromMotion">true</bool>
+
+    <!-- True if the device supports system decorations on secondary displays. -->
+    <bool name="config_supportsSystemDecorsOnSecondaryDisplays">false</bool>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index f16881a..00dd3db 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -154,12 +154,12 @@
     <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:无法转接"</string>
     <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:无法转接"</string>
     <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"移动网络安全"</string>
-    <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"加密,连接到未加密网络时通知"</string>
+    <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"加密、通知(连接到未加密网络时)"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"有网络获取了设备 ID"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>,当您使用<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM 卡时,一个附近的网络记录了您设备的唯一 ID(IMSI 或 IMEI)"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>,当您使用<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM 卡时,一个附近的网络记录了您设备的唯一 ID(IMSI 或 IMEI)。\n\n这意味着您的位置信息、活动或身份信息都被记录了。这是常见做法,但对注重隐私的人来说可能是一个问题。"</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"连接到了名为“<xliff:g id="NETWORK_NAME">%1$s</xliff:g>”的加密网络"</string>
-    <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM 卡连接现在更安全了"</string>
+    <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM 卡的连接现在更安全了"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"连接到了一个未加密的网络"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"目前,当您使用<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM 卡时,通话、消息和数据更易受到攻击"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"目前,当您使用<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM 卡时,通话、消息和数据更易受到攻击。\n\n当您的连接再次加密时,您会另收到一条通知。"</string>
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"打开“信息”应用"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"运作方式"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待归档…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index efa260f..142940a 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 0c18193..b7ee23c 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -2144,10 +2144,8 @@
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"日常安排模式資訊通知"</string>
     <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"已開啟省電模式"</string>
     <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"降低電池用量,以便延長電池續航力"</string>
-    <!-- no translation found for dynamic_mode_notification_title_v2 (5072385242078021152) -->
-    <skip />
-    <!-- no translation found for dynamic_mode_notification_summary_v2 (2142444344663147938) -->
-    <skip />
+    <string name="dynamic_mode_notification_title_v2" msgid="5072385242078021152">"省電模式已開啟"</string>
+    <string name="dynamic_mode_notification_summary_v2" msgid="2142444344663147938">"已開啟省電模式,延長電池續航力"</string>
     <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"省電模式"</string>
     <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"省電模式已關閉"</string>
     <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"手機電力充足,各項功能不再受到限制。"</string>
@@ -2415,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」應用程式"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 7dc3fd6..a967fb6 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -2413,4 +2413,22 @@
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Vula Imilayezo"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Indlela esebenza ngayo"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ilindile..."</string>
+    <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
+    <skip />
+    <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
+    <skip />
+    <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
+    <skip />
+    <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 37d39a7..5fa13ba 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1346,6 +1346,51 @@
         <attr name="materialColorTertiary" format="color"/>
         <!-- The error color for the app, intended to draw attention to error conditions. @hide -->
         <attr name="materialColorError" format="color"/>
+
+        <!-- System Custom Tokens-->
+        <!-- @hide -->
+        <attr name="customColorWidgetBackground" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorClockHour" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorClockMinute" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorClockSecond" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorThemeApp" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnThemeApp" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorThemeAppRing" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnThemeAppRing" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorBrandA" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorBrandB" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorBrandC" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorBrandD" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorUnderSurface" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorShadeActive" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnShadeActive" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnShadeActiveVariant" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorShadeInactive" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnShadeInactive" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnShadeInactiveVariant" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorShadeDisabled" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOverviewBackground" format="color"/>
+
     </declare-styleable>
 
     <!-- **************************************************************** -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index e671919..5e039b5 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -581,6 +581,51 @@
     <color name="system_on_tertiary_fixed">#FFFFFF</color>
     <color name="system_on_tertiary_fixed_variant">#FFFFFF</color>
 
+    <!--Colors used in Android system, from design system. These values can be overlaid at runtime
+     by OverlayManager RROs.-->
+    <color name="system_widget_background_light">#EEF0FF</color>
+    <color name="system_clock_hour_light">#1D2435</color>
+    <color name="system_clock_minute_light">#20386A</color>
+    <color name="system_clock_second_light">#000000</color>
+    <color name="system_theme_app_light">#2F4578</color>
+    <color name="system_on_theme_app_light">#D6DFFF</color>
+    <color name="system_theme_app_ring_light">#94AAE4</color>
+    <color name="system_on_theme_app_ring_light">#FDD7FA</color>
+    <color name="system_brand_a_light">#3A5084</color>
+    <color name="system_brand_b_light">#6E7488</color>
+    <color name="system_brand_c_light">#6076AC</color>
+    <color name="system_brand_d_light">#8C6D8C</color>
+    <color name="system_under_surface_light">#000000</color>
+    <color name="system_shade_active_light">#D9E2FF</color>
+    <color name="system_on_shade_active_light">#152E60</color>
+    <color name="system_on_shade_active_variant_light">#2F4578</color>
+    <color name="system_shade_inactive_light">#2F3036</color>
+    <color name="system_on_shade_inactive_light">#E1E2EC</color>
+    <color name="system_on_shade_inactive_variant_light">#C5C6D0</color>
+    <color name="system_shade_disabled_light">#0C0E13</color>
+    <color name="system_overview_background_light">#50525A</color>
+    <color name="system_widget_background_dark">#152E60</color>
+    <color name="system_clock_hour_dark">#9AA0B6</color>
+    <color name="system_clock_minute_dark">#D8E1FF</color>
+    <color name="system_clock_second_dark">#FFFFFF</color>
+    <color name="system_theme_app_dark">#D9E2FF</color>
+    <color name="system_on_theme_app_dark">#304679</color>
+    <color name="system_theme_app_ring_dark">#94AAE4</color>
+    <color name="system_on_theme_app_ring_dark">#E0BBDD</color>
+    <color name="system_brand_a_dark">#90A6DF</color>
+    <color name="system_brand_b_dark">#A4ABC1</color>
+    <color name="system_brand_c_dark">#7A90C8</color>
+    <color name="system_brand_d_dark">#A886A6</color>
+    <color name="system_under_surface_dark">#000000</color>
+    <color name="system_shade_active_dark">#D9E2FF</color>
+    <color name="system_on_shade_active_dark">#001945</color>
+    <color name="system_on_shade_active_variant_dark">#2F4578</color>
+    <color name="system_shade_inactive_dark">#2F3036</color>
+    <color name="system_on_shade_inactive_dark">#E1E2EC</color>
+    <color name="system_on_shade_inactive_variant_dark">#C5C6D0</color>
+    <color name="system_shade_disabled_dark">#0C0E13</color>
+    <color name="system_overview_background_dark">#C5C6D0</color>
+
     <!-- Accessibility shortcut icon background color -->
     <color name="accessibility_feature_background">#5F6368</color> <!-- Google grey 700 -->
     <color name="accessibility_magnification_background">#F50D60</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cefc648..5bd2033 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4444,8 +4444,8 @@
     <!-- True if camera app should be pinned via Pinner Service -->
     <bool name="config_pinnerCameraApp">false</bool>
 
-    <!-- True if home app should be pinned via Pinner Service -->
-    <bool name="config_pinnerHomeApp">false</bool>
+    <!-- Bytes that the PinnerService will pin for Home app -->
+    <integer name="config_pinnerHomePinBytes">0</integer>
 
     <!-- True if assistant app should be pinned via Pinner Service -->
     <bool name="config_pinnerAssistantApp">false</bool>
@@ -4652,6 +4652,19 @@
      -->
     <string-array name="config_companionDeviceCerts" translatable="false"></string-array>
 
+    <!-- A list of packages that auto-enable permissions sync feature.
+         Note that config_companionPermSyncEnabledPackages and config_companionPermSyncEnabledCerts
+         are parallel arrays.
+     -->
+    <string-array name="config_companionPermSyncEnabledPackages" translatable="false"></string-array>
+
+    <!-- A list of SHA256 Certificates corresponding to config_companionPermSyncEnabledPackages.
+         Note that config_companionPermSyncEnabledPackages and config_companionPermSyncEnabledCerts
+         are parallel arrays.
+         Example: "1A:2B:3C:4D"
+     -->
+    <string-array name="config_companionPermSyncEnabledCerts" translatable="false"></string-array>
+
     <!-- The package name for the default wellbeing app.
          This package must be trusted, as it has the permissions to control other applications
          on the device.
@@ -4704,6 +4717,13 @@
     <!-- The component name for the default system on-device sandboxed inference service. -->
     <string name="config_defaultOnDeviceSandboxedInferenceService" translatable="false"></string>
 
+    <!-- The broadcast intent name for notifying when the on-device model is loading  -->
+    <string name="config_onDeviceIntelligenceModelLoadedBroadcastKey" translatable="false"></string>
+
+    <!-- The broadcast intent name for notifying when the on-device model has been unloaded  -->
+    <string name="config_onDeviceIntelligenceModelUnloadedBroadcastKey" translatable="false"></string>
+
+
     <!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent for
          wearable sensing. -->
     <string translatable="false" name="config_defaultWearableSensingConsentComponent"></string>
diff --git a/core/res/res/values/config_battery_stats.xml b/core/res/res/values/config_battery_stats.xml
index 8d97362..80cf088 100644
--- a/core/res/res/values/config_battery_stats.xml
+++ b/core/res/res/values/config_battery_stats.xml
@@ -27,16 +27,18 @@
     <!-- Whether to reset Battery Stats on unplug if the battery was significantly charged -->
     <bool name="config_batteryStatsResetOnUnplugAfterSignificantCharge">true</bool>
 
-    <!-- CPU power stats collection throttle period in milliseconds.  Since power stats collection
-    is a relatively expensive operation, this throttle period may need to be adjusted for low-power
-    devices-->
-    <integer name="config_defaultPowerStatsThrottlePeriodCpu">60000</integer>
+    <!-- Power stats collection throttle periods in milliseconds.  Since power stats collection
+    is a relatively expensive operation, these throttle period may need to be adjusted for low-power
+    devices.
 
-    <!-- Mobile Radio power stats collection throttle period in milliseconds. -->
-    <integer name="config_defaultPowerStatsThrottlePeriodMobileRadio">3600000</integer>
-
-    <!-- Mobile Radio power stats collection throttle period in milliseconds. -->
-    <integer name="config_defaultPowerStatsThrottlePeriodWifi">3600000</integer>
+    The syntax of this config string is as follows:
+    <pre>
+       power-component-name1:throttle-period-millis1 power-component-name2:throttle-period-ms2 ...
+    </pre>
+    Use "*" for the power-component-name to represent the default for all power components
+    not mentioned by name.
+    -->
+    <string name="config_powerStatsThrottlePeriods">cpu:60000 *:300000</string>
 
     <!-- PowerStats aggregation period in milliseconds. This is the interval at which the power
     stats aggregation procedure is performed and the results stored in PowerStatsStore. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 59e4161..28678c1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -794,6 +794,9 @@
     <!-- The divider symbol between different parts of the notification header including spaces. not translatable [CHAR LIMIT=3] -->
     <string name="notification_header_divider_symbol_with_spaces" translatable="false">" • "</string>
 
+    <!-- Text for inline reply button for compact conversation heads ups -->
+    <string name="notification_compact_heads_up_reply">Reply</string>
+
     <!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
     <string name="notification_hidden_text">New notification</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d058fb1..acd3b37 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -675,6 +675,8 @@
   <java-symbol type="string" name="config_companionDeviceManagerPackage" />
   <java-symbol type="array" name="config_companionDevicePackages" />
   <java-symbol type="array" name="config_companionDeviceCerts" />
+  <java-symbol type="array" name="config_companionPermSyncEnabledPackages" />
+  <java-symbol type="array" name="config_companionPermSyncEnabledCerts" />
   <java-symbol type="string" name="config_default_dns_server" />
   <java-symbol type="string" name="config_ethernet_iface_regex" />
   <java-symbol type="string" name="not_checked" />
@@ -2351,6 +2353,7 @@
   <java-symbol type="layout" name="notification_template_material_base" />
   <java-symbol type="layout" name="notification_template_material_heads_up_base" />
   <java-symbol type="layout" name="notification_template_material_compact_heads_up_base" />
+  <java-symbol type="layout" name="notification_template_material_messaging_compact_heads_up" />
   <java-symbol type="layout" name="notification_template_material_big_base" />
   <java-symbol type="layout" name="notification_template_material_big_picture" />
   <java-symbol type="layout" name="notification_template_material_inbox" />
@@ -3437,7 +3440,7 @@
   <!-- Pinner Service -->
   <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
   <java-symbol type="bool" name="config_pinnerCameraApp" />
-  <java-symbol type="bool" name="config_pinnerHomeApp" />
+  <java-symbol type="integer" name="config_pinnerHomePinBytes" />
   <java-symbol type="bool" name="config_pinnerAssistantApp" />
   <java-symbol type="integer" name="config_pinnerWebviewPinBytes" />
 
@@ -3626,6 +3629,7 @@
   <java-symbol type="drawable" name="lockscreen_selected" />
 
   <java-symbol type="string" name="notification_header_divider_symbol_with_spaces" />
+  <java-symbol type="string" name="notification_compact_heads_up_reply" />
 
   <java-symbol type="color" name="notification_primary_text_color_light" />
   <java-symbol type="color" name="notification_primary_text_color_dark" />
@@ -3941,6 +3945,8 @@
   <java-symbol type="string" name="config_defaultWearableSensingService" />
   <java-symbol type="string" name="config_defaultOnDeviceIntelligenceService" />
   <java-symbol type="string" name="config_defaultOnDeviceSandboxedInferenceService" />
+  <java-symbol type="string" name="config_onDeviceIntelligenceModelLoadedBroadcastKey" />
+  <java-symbol type="string" name="config_onDeviceIntelligenceModelUnloadedBroadcastKey" />
   <java-symbol type="string" name="config_retailDemoPackage" />
   <java-symbol type="string" name="config_retailDemoPackageSignature" />
 
@@ -4518,6 +4524,7 @@
   <java-symbol type="id" name="expand_button_container" />
   <java-symbol type="id" name="expand_button_a11y_container" />
   <java-symbol type="id" name="expand_button_touch_container" />
+  <java-symbol type="id" name="reply_action_container" />
   <java-symbol type="id" name="messaging_group_content_container" />
   <java-symbol type="id" name="expand_button_and_content_container" />
   <java-symbol type="id" name="conversation_header" />
@@ -5236,9 +5243,7 @@
 
   <java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" />
   <java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" />
-  <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodCpu" />
-  <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodMobileRadio" />
-  <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodWifi" />
+  <java-symbol type="string" name="config_powerStatsThrottlePeriods" />
   <java-symbol type="integer" name="config_powerStatsAggregationPeriod" />
   <java-symbol type="integer" name="config_aggregatedPowerStatsSpanDuration" />
 
@@ -5288,6 +5293,71 @@
   <java-symbol name="materialColorTertiary" type="attr"/>
   <java-symbol name="materialColorError" type="attr"/>
 
+  <java-symbol name="customColorWidgetBackground" type="attr"/>
+  <java-symbol name="customColorClockHour" type="attr"/>
+  <java-symbol name="customColorClockMinute" type="attr"/>
+  <java-symbol name="customColorClockSecond" type="attr"/>
+  <java-symbol name="customColorThemeApp" type="attr"/>
+  <java-symbol name="customColorOnThemeApp" type="attr"/>
+  <java-symbol name="customColorThemeAppRing" type="attr"/>
+  <java-symbol name="customColorOnThemeAppRing" type="attr"/>
+  <java-symbol name="customColorBrandA" type="attr"/>
+  <java-symbol name="customColorBrandB" type="attr"/>
+  <java-symbol name="customColorBrandC" type="attr"/>
+  <java-symbol name="customColorBrandD" type="attr"/>
+  <java-symbol name="customColorUnderSurface" type="attr"/>
+  <java-symbol name="customColorShadeActive" type="attr"/>
+  <java-symbol name="customColorOnShadeActive" type="attr"/>
+  <java-symbol name="customColorOnShadeActiveVariant" type="attr"/>
+  <java-symbol name="customColorShadeInactive" type="attr"/>
+  <java-symbol name="customColorOnShadeInactive" type="attr"/>
+  <java-symbol name="customColorOnShadeInactiveVariant" type="attr"/>
+  <java-symbol name="customColorShadeDisabled" type="attr"/>
+  <java-symbol name="customColorOverviewBackground" type="attr"/>
+
+  <java-symbol name="system_widget_background_light" type="color"/>
+  <java-symbol name="system_clock_hour_light" type="color"/>
+  <java-symbol name="system_clock_minute_light" type="color"/>
+  <java-symbol name="system_clock_second_light" type="color"/>
+  <java-symbol name="system_theme_app_light" type="color"/>
+  <java-symbol name="system_on_theme_app_light" type="color"/>
+  <java-symbol name="system_theme_app_ring_light" type="color"/>
+  <java-symbol name="system_on_theme_app_ring_light" type="color"/>
+  <java-symbol name="system_brand_a_light" type="color"/>
+  <java-symbol name="system_brand_b_light" type="color"/>
+  <java-symbol name="system_brand_c_light" type="color"/>
+  <java-symbol name="system_brand_d_light" type="color"/>
+  <java-symbol name="system_under_surface_light" type="color"/>
+  <java-symbol name="system_shade_active_light" type="color"/>
+  <java-symbol name="system_on_shade_active_light" type="color"/>
+  <java-symbol name="system_on_shade_active_variant_light" type="color"/>
+  <java-symbol name="system_shade_inactive_light" type="color"/>
+  <java-symbol name="system_on_shade_inactive_light" type="color"/>
+  <java-symbol name="system_on_shade_inactive_variant_light" type="color"/>
+  <java-symbol name="system_shade_disabled_light" type="color"/>
+  <java-symbol name="system_overview_background_light" type="color"/>
+  <java-symbol name="system_widget_background_dark" type="color"/>
+  <java-symbol name="system_clock_hour_dark" type="color"/>
+  <java-symbol name="system_clock_minute_dark" type="color"/>
+  <java-symbol name="system_clock_second_dark" type="color"/>
+  <java-symbol name="system_theme_app_dark" type="color"/>
+  <java-symbol name="system_on_theme_app_dark" type="color"/>
+  <java-symbol name="system_theme_app_ring_dark" type="color"/>
+  <java-symbol name="system_on_theme_app_ring_dark" type="color"/>
+  <java-symbol name="system_brand_a_dark" type="color"/>
+  <java-symbol name="system_brand_b_dark" type="color"/>
+  <java-symbol name="system_brand_c_dark" type="color"/>
+  <java-symbol name="system_brand_d_dark" type="color"/>
+  <java-symbol name="system_under_surface_dark" type="color"/>
+  <java-symbol name="system_shade_active_dark" type="color"/>
+  <java-symbol name="system_on_shade_active_dark" type="color"/>
+  <java-symbol name="system_on_shade_active_variant_dark" type="color"/>
+  <java-symbol name="system_shade_inactive_dark" type="color"/>
+  <java-symbol name="system_on_shade_inactive_dark" type="color"/>
+  <java-symbol name="system_on_shade_inactive_variant_dark" type="color"/>
+  <java-symbol name="system_shade_disabled_dark" type="color"/>
+  <java-symbol name="system_overview_background_dark" type="color"/>
+
   <java-symbol type="attr" name="actionModeUndoDrawable" />
   <java-symbol type="attr" name="actionModeRedoDrawable" />
 
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index ee19144..24d4938 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -284,6 +284,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -380,6 +402,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar.  This theme
@@ -475,6 +519,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -572,6 +638,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -668,6 +756,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -772,6 +882,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -867,6 +999,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -961,6 +1115,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -1056,6 +1232,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -1167,6 +1365,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -1263,6 +1483,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -1357,6 +1599,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -1453,6 +1717,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1548,6 +1834,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1643,6 +1951,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -1738,6 +2068,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -1833,6 +2185,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -1928,6 +2302,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Theme for the dialog shown when an app crashes or ANRs. -->
@@ -2028,6 +2424,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -2121,6 +2539,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -2352,6 +2792,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
@@ -2447,6 +2909,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -2541,6 +3025,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -2636,6 +3142,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -2733,6 +3261,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -2829,6 +3379,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -2931,6 +3503,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
@@ -3029,6 +3623,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
@@ -3126,6 +3742,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -3224,6 +3862,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -3303,6 +3963,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -3382,6 +4064,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -3480,6 +4184,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -3579,6 +4305,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -3676,6 +4424,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -3772,6 +4542,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -3867,6 +4659,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -3962,6 +4776,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -4055,6 +4891,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
@@ -4156,6 +5014,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.SystemUI" parent="Theme.DeviceDefault.Light">
@@ -4238,6 +5118,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.SystemUI.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
@@ -4312,6 +5214,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
@@ -4407,6 +5331,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.DialogBase" parent="Theme.Material.Light.BaseDialog">
@@ -4486,6 +5432,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.DeviceDefault.Settings.DialogBase">
@@ -4605,6 +5573,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -4702,6 +5692,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -4825,6 +5837,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <style name="ThemeOverlay.DeviceDefault.Accent.Light">
@@ -4878,6 +5912,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
@@ -4935,6 +5991,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen">
@@ -4988,6 +6066,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
@@ -5052,6 +6152,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
     <style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight">
         <item name="colorListDivider">@color/list_divider_opacity_device_default_light</item>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 436ba15..eb3c84a 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -260,6 +260,7 @@
         "src/com/android/internal/os/**/*.java",
         "src/com/android/internal/util/**/*.java",
         "src/com/android/internal/power/EnergyConsumerStatsTest.java",
+        "src/com/android/internal/ravenwood/**/*.java",
 
         // Pull in R.java from FrameworksCoreTests-resonly, not from FrameworksCoreTests,
         // to avoid having a dependency to FrameworksCoreTests.
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 7fb894a..30ec940 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -84,7 +84,6 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.SystemProperties;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.text.Spannable;
@@ -132,9 +131,6 @@
     @Before
     public void setUp() {
         mContext = InstrumentationRegistry.getContext();
-        // TODO(b/169435530): remove this flag set once resolved.
-        SystemProperties.set("persist.sysui.notification.builder_extras_override",
-                Boolean.toString(false));
     }
 
     @Test
@@ -1703,10 +1699,6 @@
     // Ensures that extras in a Notification Builder can be updated.
     @Test
     public void testExtras_cachedExtrasOverwrittenByUserProvided() {
-        // Sets the flag to new state.
-        // TODO(b/169435530): remove this set value once resolved.
-        SystemProperties.set("persist.sysui.notification.builder_extras_override",
-                Boolean.toString(true));
         Bundle extras = new Bundle();
         extras.putCharSequence(EXTRA_TITLE, "test title");
         extras.putCharSequence(EXTRA_SUMMARY_TEXT, "summary text");
@@ -1732,10 +1724,6 @@
     // Ensures that extras in a Notification Builder can be updated by an extender.
     @Test
     public void testExtras_cachedExtrasOverwrittenByExtender() {
-        // Sets the flag to new state.
-        // TODO(b/169435530): remove this set value once resolved.
-        SystemProperties.set("persist.sysui.notification.builder_extras_override",
-                Boolean.toString(true));
         Notification.CarExtender extender = new Notification.CarExtender().setColor(1234);
 
         Notification notification = new Notification.Builder(mContext, "test id")
@@ -1749,58 +1737,6 @@
         assertThat(recoveredExtender.getColor()).isEqualTo(5678);
     }
 
-    // Validates pre-flag flip behavior, that extras in a Notification Builder cannot be updated.
-    // TODO(b/169435530): remove this test once resolved.
-    @Test
-    public void testExtras_cachedExtrasOverwrittenByUserProvidedOld() {
-        // Sets the flag to old state.
-        SystemProperties.set("persist.sysui.notification.builder_extras_override",
-                Boolean.toString(false));
-
-        Bundle extras = new Bundle();
-        extras.putCharSequence(EXTRA_TITLE, "test title");
-        extras.putCharSequence(EXTRA_SUMMARY_TEXT, "summary text");
-
-        Notification.Builder builder = new Notification.Builder(mContext, "test id")
-                .addExtras(extras);
-
-        Notification notification = builder.build();
-        assertThat(notification.extras.getCharSequence(EXTRA_TITLE).toString()).isEqualTo(
-                "test title");
-        assertThat(notification.extras.getCharSequence(EXTRA_SUMMARY_TEXT).toString()).isEqualTo(
-                "summary text");
-
-        extras.putCharSequence(EXTRA_TITLE, "new title");
-        builder.addExtras(extras);
-        notification = builder.build();
-        assertThat(notification.extras.getCharSequence(EXTRA_TITLE).toString()).isEqualTo(
-                "test title");
-        assertThat(notification.extras.getCharSequence(EXTRA_SUMMARY_TEXT).toString()).isEqualTo(
-                "summary text");
-    }
-
-    // Validates pre-flag flip behavior, that extras in a Notification Builder cannot be updated
-    // by an extender.
-    // TODO(b/169435530): remove this test once resolved.
-    @Test
-    public void testExtras_cachedExtrasOverwrittenByExtenderOld() {
-        // Sets the flag to old state.
-        SystemProperties.set("persist.sysui.notification.builder_extras_override",
-                Boolean.toString(false));
-
-        Notification.CarExtender extender = new Notification.CarExtender().setColor(1234);
-
-        Notification notification = new Notification.Builder(mContext, "test id")
-                .extend(extender).build();
-
-        extender.setColor(5678);
-
-        Notification.Builder.recoverBuilder(mContext, notification).extend(extender).build();
-
-        Notification.CarExtender recoveredExtender = new Notification.CarExtender(notification);
-        assertThat(recoveredExtender.getColor()).isEqualTo(1234);
-    }
-
     @Test
     @CoreCompatChangeRule.EnableCompatChanges({Notification.WEARABLE_EXTENDER_BACKGROUND_BLOCKED})
     public void wearableBackgroundBlockEnabled_wearableBackgroundSet_valueRemainsNull() {
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index b6f4429..ee1d1e1 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -65,6 +66,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.concurrent.RejectedExecutionException;
 import java.util.function.BiConsumer;
 
 /**
@@ -221,4 +223,17 @@
                 123 /* newDisplayId */, true /* shouldReportConfigChange*/);
         inOrder.verify(mController).onContextConfigurationPostChanged(context);
     }
+
+    @Test
+    public void testDisplayListenerHandlerClosed() {
+        doReturn(123).when(mActivity).getDisplayId();
+        doThrow(new RejectedExecutionException()).when(mController).onDisplayChanged(123);
+
+        mController.onContextConfigurationPreChanged(mActivity);
+        mConfiguration.windowConfiguration.setMaxBounds(new Rect(0, 0, 100, 200));
+        mController.onContextConfigurationPostChanged(mActivity);
+
+        // No crash
+        verify(mController).onDisplayChanged(123);
+    }
 }
diff --git a/core/tests/coretests/src/android/graphics/drawable/IconTest.java b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
index 950925f..e0c3b04 100644
--- a/core/tests/coretests/src/android/graphics/drawable/IconTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
@@ -475,8 +475,8 @@
         final Icon ic = Icon.createWithBitmap(bm);
         final Drawable drawable = ic.loadDrawable(mContext);
 
-        assertThat(drawable.getIntrinsicWidth()).isEqualTo(maxWidth);
-        assertThat(drawable.getIntrinsicHeight()).isEqualTo(maxHeight);
+        assertThat(Math.abs(drawable.getIntrinsicWidth() - maxWidth)).isLessThan(2);
+        assertThat(Math.abs(drawable.getIntrinsicHeight() - maxHeight)).isLessThan(2);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/net/OWNERS b/core/tests/coretests/src/android/net/OWNERS
index a779c00..beb77dc 100644
--- a/core/tests/coretests/src/android/net/OWNERS
+++ b/core/tests/coretests/src/android/net/OWNERS
@@ -1,4 +1,5 @@
 include /services/core/java/com/android/server/net/OWNERS
 
-per-file SSL*,Uri*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com
+per-file SSL*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com
 per-file SntpClient* = file:/services/core/java/com/android/server/timedetector/OWNERS
+per-file Uri* = varunshah@google.com
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index 2a4ca79..57cb158 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -18,6 +18,7 @@
 
 import android.content.ContentUris;
 import android.os.Parcel;
+import android.platform.test.annotations.AsbSecurityTest;
 
 import androidx.test.filters.SmallTest;
 
@@ -86,6 +87,16 @@
         assertNull(u.getHost());
     }
 
+    @AsbSecurityTest(cveBugId = 261721900)
+    @SmallTest
+    public void testSchemeSanitization() {
+        Uri uri = new Uri.Builder()
+                .scheme("http://https://evil.com:/te:st/")
+                .authority("google.com").path("one/way").build();
+        assertEquals("httphttpsevil.com:/te:st/", uri.getScheme());
+        assertEquals("httphttpsevil.com:/te:st/://google.com/one/way", uri.toString());
+    }
+
     @SmallTest
     public void testStringUri() {
         assertEquals("bob lee",
diff --git a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
index 6ae3d65..df9a89e 100644
--- a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
+++ b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
@@ -674,8 +674,6 @@
                             protoOutputStream.write(SINGLE_INT, singleIntValue);
                             protoOutputStream.end(payloadToken);
                             protoOutputStream.end(forTestingToken);
-
-                            ctx.flush();
                         }),
                         (args) -> {}
                 );
diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
index 57bbb1c..5917cc1 100644
--- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
+++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
@@ -37,6 +37,7 @@
 import android.content.Context;
 import android.graphics.Insets;
 import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
 import android.view.animation.BackGestureInterpolator;
 import android.view.animation.Interpolator;
 import android.view.inputmethod.InputMethodManager;
@@ -54,6 +55,8 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.lang.reflect.Field;
+
 /**
  * Tests for {@link ImeBackAnimationController}.
  *
@@ -104,6 +107,13 @@
             when(mInsetsController.getHost()).thenReturn(mViewRootInsetsControllerHost);
             when(mViewRootInsetsControllerHost.getInputMethodManager()).thenReturn(
                     inputMethodManager);
+            try {
+                Field field = InsetsController.class.getDeclaredField("mSourceConsumers");
+                field.setAccessible(true);
+                field.set(mInsetsController, new SparseArray<InsetsSourceConsumer>());
+            } catch (NoSuchFieldException | IllegalAccessException e) {
+                throw new RuntimeException("Unable to set mSourceConsumers", e);
+            }
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index bc0ae9f..0b1b40c 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -623,28 +623,6 @@
         assertEquals(FRAME_RATE_CATEGORY_HIGH_HINT, mViewRoot.getLastPreferredFrameRateCategory());
     }
 
-    @Test
-    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
-            FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
-    })
-    public void idleDetected() throws Throwable {
-        waitForFrameRateCategoryToSettle();
-        mActivityRule.runOnUiThread(() -> {
-            mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
-            mMovingView.setFrameContentVelocity(Float.MAX_VALUE);
-            mMovingView.invalidate();
-            runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH,
-                    mViewRoot.getLastPreferredFrameRateCategory()));
-        });
-        waitForAfterDraw();
-
-        // Wait for idle timeout
-        Thread.sleep(500);
-        assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
-        assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
-                mViewRoot.getLastPreferredFrameRateCategory());
-    }
-
     private void runAfterDraw(@NonNull Runnable runnable) {
         Handler handler = new Handler(Looper.getMainLooper());
         mAfterDrawLatch = new CountDownLatch(1);
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigParserTest.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigParserTest.java
new file mode 100644
index 0000000..4eccbe5
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigParserTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.content.res;
+
+import static com.android.internal.content.om.OverlayConfigParser.SysPropWrapper;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.content.om.OverlayConfigParser;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class OverlayConfigParserTest {
+    @Test(expected = IllegalStateException.class)
+    public void testMergePropNotRoProp() {
+        SysPropWrapper sysProp = p -> {
+            return "dummy_value";
+        };
+        OverlayConfigParser.expandProperty("${persist.value}/path", sysProp);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testMergePropMissingEndBracket() {
+        SysPropWrapper sysProp = p -> {
+            return "dummy_value";
+        };
+        OverlayConfigParser.expandProperty("${ro.value/path", sysProp);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testMergeOnlyPropStart() {
+        SysPropWrapper sysProp = p -> {
+            return "dummy_value";
+        };
+        OverlayConfigParser.expandProperty("path/${", sysProp);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testMergePropInProp() {
+        SysPropWrapper sysProp = p -> {
+            return "dummy_value";
+        };
+        OverlayConfigParser.expandProperty("path/${${ro.value}}", sysProp);
+    }
+
+    /**
+     * The path is only allowed to contain one property.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testMergePropMultipleProps() {
+        SysPropWrapper sysProp = p -> {
+            return "dummy_value";
+        };
+        OverlayConfigParser.expandProperty("${ro.value}/path${ro.value2}/path", sysProp);
+    }
+
+    @Test
+    public void testMergePropOneProp() {
+        final SysPropWrapper sysProp = p -> {
+            if ("ro.value".equals(p)) {
+                return "dummy_value";
+            } else {
+                return "invalid";
+            }
+        };
+
+        // Property in the beginnig of the string
+        String result = OverlayConfigParser.expandProperty("${ro.value}/path",
+                sysProp);
+        assertEquals("dummy_value/path", result);
+
+        // Property in the middle of the string
+        result = OverlayConfigParser.expandProperty("path/${ro.value}/file",
+                sysProp);
+        assertEquals("path/dummy_value/file", result);
+
+        // Property at the of the string
+        result = OverlayConfigParser.expandProperty("path/${ro.value}",
+                sysProp);
+        assertEquals("path/dummy_value", result);
+
+        // Property is the entire string
+        result = OverlayConfigParser.expandProperty("${ro.value}",
+                sysProp);
+        assertEquals("dummy_value", result);
+    }
+
+    @Test
+    public void testMergePropNoProp() {
+        final SysPropWrapper sysProp = p -> {
+            return "dummy_value";
+        };
+
+        final String path = "no_props/path";
+        String result = OverlayConfigParser.expandProperty(path, sysProp);
+        assertEquals(path, result);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
index baab3b2..4846ed27 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
@@ -22,6 +22,7 @@
 import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.IndentingPrintWriter;
 import android.util.SparseArray;
 import android.util.Xml;
 
@@ -31,6 +32,8 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 
+import com.google.common.truth.StringSubject;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -38,6 +41,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.StringWriter;
 import java.nio.charset.StandardCharsets;
 
 @RunWith(AndroidJUnit4.class)
@@ -56,6 +60,9 @@
         extras.putBoolean("hasPowerMonitor", true);
         SparseArray<String> stateLabels = new SparseArray<>();
         stateLabels.put(0x0F, "idle");
+        extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, "device:0[3]");
+        extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, "state:0");
+        extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, "a:0 b:1");
         mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, stateLabels,
                 1, 2, extras);
         mRegistry.register(mDescriptor);
@@ -191,4 +198,68 @@
         newParcel.setDataPosition(0);
         return newParcel;
     }
+
+    @Test
+    public void formatForBatteryHistory() {
+        PowerStats stats = new PowerStats(mDescriptor);
+        stats.durationMs = 1234;
+        stats.stats[0] = 10;
+        stats.stats[1] = 20;
+        stats.stats[2] = 30;
+        stats.stateStats.put(0x0F, new long[]{16});
+        stats.stateStats.put(0xF0, new long[]{17});
+        stats.uidStats.put(42, new long[]{40, 50});
+        stats.uidStats.put(99, new long[]{60, 70});
+
+        assertThat(stats.formatForBatteryHistory(" #"))
+                .isEqualTo("duration=1234 cpu="
+                        + "device: [10, 20, 30]"
+                        + " (idle) state: 16"
+                        + " (cpu-f0) state: 17"
+                        + " #42: a: 40 b: 50"
+                        + " #99: a: 60 b: 70");
+    }
+
+    @Test
+    public void dump() {
+        PowerStats stats = new PowerStats(mDescriptor);
+        stats.durationMs = 1234;
+        stats.stats[0] = 10;
+        stats.stats[1] = 20;
+        stats.stats[2] = 30;
+        stats.stateStats.put(0x0F, new long[]{16});
+        stats.stateStats.put(0xF0, new long[]{17});
+        stats.uidStats.put(42, new long[]{40, 50});
+        stats.uidStats.put(99, new long[]{60, 70});
+
+        StringWriter sw = new StringWriter();
+        IndentingPrintWriter pw = new IndentingPrintWriter(sw);
+        stats.dump(pw);
+        pw.flush();
+        String dump = sw.toString();
+
+        assertThat(dump).contains("duration=1234");
+        assertThat(dump).contains("device: [10, 20, 30]");
+        assertThat(dump).contains("(idle) state: 16");
+        assertThat(dump).contains("(cpu-f0) state: 17");
+        assertThat(dump).contains("UID 42: a: 40 b: 50");
+        assertThat(dump).contains("UID 99: a: 60 b: 70");
+    }
+
+    @Test
+    public void formatter() {
+        assertThatFormatted(new long[]{12, 34, 56}, "a:0 b:1[2]")
+                .isEqualTo("a: 12 b: [34, 56]");
+        assertThatFormatted(new long[]{12, 0, 0}, "a:0? b:1[2]?")
+                .isEqualTo("a: 12");
+        assertThatFormatted(new long[]{0, 34, 56}, "a:0? b:1[2]?")
+                .isEqualTo("b: [34, 56]");
+        assertThatFormatted(new long[]{3141592, 2000000, 1414213}, "pi:0p sqrt:1[2]p")
+                .isEqualTo("pi: 3.14 sqrt: [2.00, 1.41]");
+    }
+
+    private static StringSubject assertThatFormatted(long[] stats, String format) {
+        return assertThat(new PowerStats.PowerStatsFormatter(format)
+                .format(stats));
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java b/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java
new file mode 100644
index 0000000..d1ef61b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.ravenwood;
+
+import static junit.framework.TestCase.assertEquals;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodEnvironmentTest {
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+    @Test
+    public void testIsRunningOnRavenwood() {
+        assertEquals(RavenwoodRule.isUnderRavenwood(),
+                RavenwoodEnvironment.getInstance().isRunningOnRavenwood());
+    }
+}
diff --git a/data/etc/core.protolog.pb b/data/etc/core.protolog.pb
index 000f6ef..b41a607 100644
--- a/data/etc/core.protolog.pb
+++ b/data/etc/core.protolog.pb
Binary files differ
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 01deb49..b93cd46 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -583,6 +583,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "7211222997110112110": {
+      "message": "Refreshing activity for freeform camera compatibility treatment, activityRecord=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/ActivityRefresher.java"
+    },
     "1665699123574159131": {
       "message": "Starting activity when config will change = %b",
       "level": "VERBOSE",
@@ -1771,12 +1777,6 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java"
     },
-    "-7756685416834187936": {
-      "message": "Refreshing activity for camera compatibility treatment, activityRecord=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java"
-    },
     "-5176775281239247368": {
       "message": "Reverting orientation after camera compat force rotation",
       "level": "VERBOSE",
diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml
index ac1b064..a4ee825 100644
--- a/data/fonts/font_fallback_cjkvf.xml
+++ b/data/fonts/font_fallback_cjkvf.xml
@@ -768,7 +768,7 @@
         <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
     </family>
     <family lang="zh-Hans">
-        <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"
+        <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"
             supportedAxes="wght">
             NotoSansCJK-Regular.ttc
             <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
@@ -780,7 +780,7 @@
         </font>
     </family>
     <family lang="zh-Hant,zh-Bopo">
-        <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"
+        <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"
             supportedAxes="wght">
             NotoSansCJK-Regular.ttc
             <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
@@ -792,7 +792,7 @@
         </font>
     </family>
     <family lang="ja">
-        <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"
+        <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"
             supportedAxes="wght">
             NotoSansCJK-Regular.ttc
             <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
@@ -810,7 +810,7 @@
         </font>
     </family>
     <family lang="ko">
-        <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"
+        <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"
             supportedAxes="wght">
             NotoSansCJK-Regular.ttc
             <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
diff --git a/data/fonts/fonts_cjkvf.xml b/data/fonts/fonts_cjkvf.xml
index 9545ae7..8cbc300 100644
--- a/data/fonts/fonts_cjkvf.xml
+++ b/data/fonts/fonts_cjkvf.xml
@@ -1409,39 +1409,39 @@
         <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
     </family>
     <family lang="zh-Hans">
-        <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="100"/>
         </font>
-        <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="200"/>
         </font>
-        <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="300"/>
         </font>
-        <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="800"/>
         </font>
-        <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="900"/>
         </font>
@@ -1450,39 +1450,39 @@
         </font>
     </family>
     <family lang="zh-Hant,zh-Bopo">
-        <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="100"/>
         </font>
-        <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="200"/>
         </font>
-        <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="300"/>
         </font>
-        <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="800"/>
         </font>
-        <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="900"/>
         </font>
@@ -1491,39 +1491,39 @@
         </font>
     </family>
     <family lang="ja">
-        <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="100"/>
         </font>
-        <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="200"/>
         </font>
-        <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="300"/>
         </font>
-        <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="800"/>
         </font>
-        <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="900"/>
         </font>
@@ -1542,39 +1542,39 @@
         </font>
     </family>
     <family lang="ko">
-        <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="100"/>
         </font>
-        <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="200"/>
         </font>
-        <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="300"/>
         </font>
-        <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="800"/>
         </font>
-        <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+        <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
             <axis tag="wght" stylevalue="900"/>
         </font>
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index e8b4104..f8d3bff 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -438,9 +438,15 @@
 key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP     FALLBACK_USAGE_MAPPING
 key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN   FALLBACK_USAGE_MAPPING
 key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE FALLBACK_USAGE_MAPPING
+key usage 0x0c00D9 EMOJI_PICKER              FALLBACK_USAGE_MAPPING
 key usage 0x0c0173 MEDIA_AUDIO_TRACK         FALLBACK_USAGE_MAPPING
 key usage 0x0c019C PROFILE_SWITCH            FALLBACK_USAGE_MAPPING
+key usage 0x0c019F SETTINGS                  FALLBACK_USAGE_MAPPING
 key usage 0x0c01A2 ALL_APPS                  FALLBACK_USAGE_MAPPING
+key usage 0x0c0227 REFRESH                   FALLBACK_USAGE_MAPPING
+key usage 0x0c029D LANGUAGE_SWITCH           FALLBACK_USAGE_MAPPING
+key usage 0x0c029F RECENT_APPS               FALLBACK_USAGE_MAPPING
+key usage 0x0c02A2 ALL_APPS                  FALLBACK_USAGE_MAPPING
 key usage 0x0d0044 STYLUS_BUTTON_PRIMARY     FALLBACK_USAGE_MAPPING
 key usage 0x0d005a STYLUS_BUTTON_SECONDARY   FALLBACK_USAGE_MAPPING
 
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index d915b74..1c20141 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -143,7 +143,7 @@
          * the decoder will try to pick the best matching config based on the
          * system's screen depth, and characteristics of the original image such
          * as if it has per-pixel alpha (requiring a config that also does).
-         * 
+         *
          * Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by
          * default.
          */
@@ -183,7 +183,7 @@
 
         /**
          * If true (which is the default), the resulting bitmap will have its
-         * color channels pre-multipled by the alpha channel.
+         * color channels pre-multiplied by the alpha channel.
          *
          * <p>This should NOT be set to false for images to be directly drawn by
          * the view system or through a {@link Canvas}. The view system and
@@ -221,9 +221,9 @@
          * if {@link #inScaled} is set (which it is by default} and this
          * density does not match {@link #inTargetDensity}, then the bitmap
          * will be scaled to the target density before being returned.
-         * 
+         *
          * <p>If this is 0,
-         * {@link BitmapFactory#decodeResource(Resources, int)}, 
+         * {@link BitmapFactory#decodeResource(Resources, int)},
          * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
          * and {@link BitmapFactory#decodeResourceStream}
          * will fill in the density associated with the resource.  The other
@@ -242,29 +242,29 @@
          * This is used in conjunction with {@link #inDensity} and
          * {@link #inScaled} to determine if and how to scale the bitmap before
          * returning it.
-         * 
+         *
          * <p>If this is 0,
-         * {@link BitmapFactory#decodeResource(Resources, int)}, 
+         * {@link BitmapFactory#decodeResource(Resources, int)},
          * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
          * and {@link BitmapFactory#decodeResourceStream}
          * will fill in the density associated the Resources object's
          * DisplayMetrics.  The other
          * functions will leave it as-is and no scaling for density will be
          * performed.
-         * 
+         *
          * @see #inDensity
          * @see #inScreenDensity
          * @see #inScaled
          * @see android.util.DisplayMetrics#densityDpi
          */
         public int inTargetDensity;
-        
+
         /**
          * The pixel density of the actual screen that is being used.  This is
          * purely for applications running in density compatibility code, where
          * {@link #inTargetDensity} is actually the density the application
          * sees rather than the real screen density.
-         * 
+         *
          * <p>By setting this, you
          * allow the loading code to avoid scaling a bitmap that is currently
          * in the screen density up/down to the compatibility density.  Instead,
@@ -274,18 +274,18 @@
          * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
          * Bitmap.getScaledHeight} to account for any different between the
          * bitmap's density and the target's density.
-         * 
+         *
          * <p>This is never set automatically for the caller by
          * {@link BitmapFactory} itself.  It must be explicitly set, since the
          * caller must deal with the resulting bitmap in a density-aware way.
-         * 
+         *
          * @see #inDensity
          * @see #inTargetDensity
          * @see #inScaled
          * @see android.util.DisplayMetrics#densityDpi
          */
         public int inScreenDensity;
-        
+
         /**
          * When this flag is set, if {@link #inDensity} and
          * {@link #inTargetDensity} are not 0, the
@@ -345,7 +345,7 @@
          * ignored.
          *
          * In {@link android.os.Build.VERSION_CODES#KITKAT} and below, this
-         * field works in conjuction with inPurgeable. If inPurgeable is false,
+         * field works in conjunction with inPurgeable. If inPurgeable is false,
          * then this field is ignored. If inPurgeable is true, then this field
          * determines whether the bitmap can share a reference to the input
          * data (inputstream, array, etc.) or if it must make a deep copy.
@@ -583,11 +583,11 @@
                 opts.inDensity = density;
             }
         }
-        
+
         if (opts.inTargetDensity == 0 && res != null) {
             opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
         }
-        
+
         return decodeStream(is, pad, opts);
     }
 
@@ -611,8 +611,8 @@
     public static Bitmap decodeResource(Resources res, int id, Options opts) {
         validate(opts);
         Bitmap bm = null;
-        InputStream is = null; 
-        
+        InputStream is = null;
+
         try {
             final TypedValue value = new TypedValue();
             is = res.openRawResource(id, value);
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index e03a1da..0b3e545 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -55,7 +55,7 @@
  * Canvas and Drawables</a> developer guide.</p></div>
  */
 public class Canvas extends BaseCanvas {
-    private static int sCompatiblityVersion = 0;
+    private static int sCompatibilityVersion = 0;
     private static boolean sCompatibilityRestore = false;
     private static boolean sCompatibilitySetBitmap = false;
 
@@ -74,7 +74,7 @@
 
     // Maximum bitmap size as defined in Skia's native code
     // (see SkCanvas.cpp, SkDraw.cpp)
-    private static final int MAXMIMUM_BITMAP_SIZE = 32766;
+    private static final int MAXIMUM_BITMAP_SIZE = 32766;
 
     // Use a Holder to allow static initialization of Canvas in the boot image.
     private static class NoImagePreloadHolder {
@@ -331,7 +331,7 @@
      * @see #getMaximumBitmapHeight()
      */
     public int getMaximumBitmapWidth() {
-        return MAXMIMUM_BITMAP_SIZE;
+        return MAXIMUM_BITMAP_SIZE;
     }
 
     /**
@@ -342,7 +342,7 @@
      * @see #getMaximumBitmapWidth()
      */
     public int getMaximumBitmapHeight() {
-        return MAXMIMUM_BITMAP_SIZE;
+        return MAXIMUM_BITMAP_SIZE;
     }
 
     // the SAVE_FLAG constants must match their native equivalents
@@ -423,7 +423,7 @@
     public static final int ALL_SAVE_FLAG = 0x1F;
 
     private static void checkValidSaveFlags(int saveFlags) {
-        if (sCompatiblityVersion >= Build.VERSION_CODES.P
+        if (sCompatibilityVersion >= Build.VERSION_CODES.P
                 && saveFlags != ALL_SAVE_FLAG) {
             throw new IllegalArgumentException(
                     "Invalid Layer Save Flag - only ALL_SAVE_FLAGS is allowed");
@@ -845,7 +845,7 @@
     }
 
     private static void checkValidClipOp(@NonNull Region.Op op) {
-        if (sCompatiblityVersion >= Build.VERSION_CODES.P
+        if (sCompatibilityVersion >= Build.VERSION_CODES.P
                 && op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE) {
             throw new IllegalArgumentException(
                     "Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed");
@@ -1435,7 +1435,7 @@
     }
 
     /*package*/ static void setCompatibilityVersion(int apiLevel) {
-        sCompatiblityVersion = apiLevel;
+        sCompatibilityVersion = apiLevel;
         sCompatibilityRestore = apiLevel < Build.VERSION_CODES.M;
         sCompatibilitySetBitmap = apiLevel < Build.VERSION_CODES.O;
         nSetCompatibilityVersion(apiLevel);
diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java
index 0f2f879..c1edafc 100644
--- a/graphics/java/android/graphics/Color.java
+++ b/graphics/java/android/graphics/Color.java
@@ -289,6 +289,9 @@
  */
 @AnyThread
 @SuppressAutoDoc
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodClassLoadHook(
+        android.ravenwood.annotation.RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
 public class Color {
     @ColorInt public static final int BLACK       = 0xFF000000;
     @ColorInt public static final int DKGRAY      = 0xFF444444;
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index a2319a5..4bc3ece 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -135,6 +135,9 @@
 @AnyThread
 @SuppressWarnings("StaticInitializerReferencesSubClass")
 @SuppressAutoDoc
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodClassLoadHook(
+        android.ravenwood.annotation.RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
 public abstract class ColorSpace {
     /**
      * Standard CIE 1931 2° illuminant A, encoded in xyY.
@@ -2490,9 +2493,16 @@
             return mNativePtr;
         }
 
-        private static native long nativeGetNativeFinalizer();
-        private static native long nativeCreate(float a, float b, float c, float d,
-                float e, float f, float g, float[] xyz);
+        /**
+         * These methods can't be put in the Rgb class directly, because ColorSpace's
+         * static initializer instantiates Rgb, whose constructor needs them, which is a variation
+         * of b/337329128.
+         */
+        static class Native {
+            static native long nativeGetNativeFinalizer();
+            static native long nativeCreate(float a, float b, float c, float d,
+                    float e, float f, float g, float[] xyz);
+        }
 
         private static DoubleUnaryOperator generateOETF(TransferParameters function) {
             if (function.isHLGish()) {
@@ -2959,7 +2969,7 @@
 
                 // This mimics the old code that was in native.
                 float[] nativeTransform = adaptToIlluminantD50(mWhitePoint, mTransform);
-                mNativePtr = nativeCreate((float) mTransferParameters.a,
+                mNativePtr = Native.nativeCreate((float) mTransferParameters.a,
                                           (float) mTransferParameters.b,
                                           (float) mTransferParameters.c,
                                           (float) mTransferParameters.d,
@@ -2975,7 +2985,7 @@
 
         private static class NoImagePreloadHolder {
             public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
-                ColorSpace.Rgb.class.getClassLoader(), nativeGetNativeFinalizer(), 0);
+                ColorSpace.Rgb.class.getClassLoader(), Native.nativeGetNativeFinalizer(), 0);
         }
 
         /**
diff --git a/graphics/java/android/graphics/ComposePathEffect.java b/graphics/java/android/graphics/ComposePathEffect.java
index 3fc9eb5..7d59ece 100644
--- a/graphics/java/android/graphics/ComposePathEffect.java
+++ b/graphics/java/android/graphics/ComposePathEffect.java
@@ -20,13 +20,13 @@
 
     /**
      * Construct a PathEffect whose effect is to apply first the inner effect
-     * and the the outer pathEffect (e.g. outer(inner(path))).
+     * and the outer pathEffect (e.g. outer(inner(path))).
      */
     public ComposePathEffect(PathEffect outerpe, PathEffect innerpe) {
         native_instance = nativeCreate(outerpe.native_instance,
                                        innerpe.native_instance);
     }
-    
+
     private static native long nativeCreate(long nativeOuterpe,
                                             long nativeInnerpe);
 }
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index c86b744..88f0e8e 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -219,7 +219,7 @@
 
     @CriticalNative
     private static native long nGetFamilyReleaseFunc();
-    // By passing -1 to weigth argument, the weight value is resolved by OS/2 table in the font.
+    // By passing -1 to weight argument, the weight value is resolved by OS/2 table in the font.
     // By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font.
     private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex,
             int weight, int isItalic);
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 17c2dd9..13c4a94 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -127,7 +127,7 @@
             parser.setInput(is, null);
             parser.nextTag();
             return readFamilies(parser, systemFontDir, oemCustomization, updatableFontMap,
-                    lastModifiedDate, configVersion, false /* filter out the non-exising files */);
+                    lastModifiedDate, configVersion, false /* filter out the non-existing files */);
         }
     }
 
@@ -254,7 +254,7 @@
      * @param parser An XML parser.
      * @param fontDir a font directory name.
      * @param updatableFontMap a updated font file map.
-     * @param allowNonExistingFile true to allow font file that doesn't exists
+     * @param allowNonExistingFile true to allow font file that doesn't exist.
      * @return a FontFamily instance. null if no font files are available in this FontFamily.
      */
     public static @Nullable FontConfig.FontFamily readFamily(XmlPullParser parser, String fontDir,
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index b3615ff..8f12828 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -24,7 +24,7 @@
 /**
  * Class that contains all the timing information for the current frame. This
  * is used in conjunction with the hardware renderer to provide
- * continous-monitoring jank events
+ * continuous-monitoring jank events
  *
  * All times in nanoseconds from CLOCK_MONOTONIC/System.nanoTime()
  *
diff --git a/graphics/java/android/graphics/Interpolator.java b/graphics/java/android/graphics/Interpolator.java
index 994fb2d..28f296d 100644
--- a/graphics/java/android/graphics/Interpolator.java
+++ b/graphics/java/android/graphics/Interpolator.java
@@ -28,13 +28,13 @@
         mFrameCount = 2;
         native_instance = nativeConstructor(valueCount, 2);
     }
-    
+
     public Interpolator(int valueCount, int frameCount) {
         mValueCount = valueCount;
         mFrameCount = frameCount;
         native_instance = nativeConstructor(valueCount, frameCount);
     }
-    
+
     /**
      * Reset the Interpolator to have the specified number of values and an
      * implicit keyFrame count of 2 (just a start and end). After this call the
@@ -43,7 +43,7 @@
     public void reset(int valueCount) {
         reset(valueCount, 2);
     }
-    
+
     /**
      * Reset the Interpolator to have the specified number of values and
      * keyFrames. After this call the values for each keyFrame must be assigned
@@ -54,20 +54,20 @@
         mFrameCount = frameCount;
         nativeReset(native_instance, valueCount, frameCount);
     }
-    
+
     public final int getKeyFrameCount() {
         return mFrameCount;
     }
-    
+
     public final int getValueCount() {
         return mValueCount;
     }
-    
+
     /**
      * Assign the keyFrame (specified by index) a time value and an array of key
-     * values (with an implicity blend array of [0, 0, 1, 1] giving linear
+     * values (with an implicitly blend array of [0, 0, 1, 1] giving linear
      * transition to the next set of key values).
-     * 
+     *
      * @param index The index of the key frame to assign
      * @param msec The time (in mililiseconds) for this key frame. Based on the
      *        SystemClock.uptimeMillis() clock
@@ -80,7 +80,7 @@
     /**
      * Assign the keyFrame (specified by index) a time value and an array of key
      * values and blend array.
-     * 
+     *
      * @param index The index of the key frame to assign
      * @param msec The time (in mililiseconds) for this key frame. Based on the
      *        SystemClock.uptimeMillis() clock
@@ -99,7 +99,7 @@
         }
         nativeSetKeyFrame(native_instance, index, msec, values, blend);
     }
-    
+
     /**
      * Set a repeat count (which may be fractional) for the interpolator, and
      * whether the interpolator should mirror its repeats. The default settings
@@ -110,7 +110,7 @@
             nativeSetRepeatMirror(native_instance, repeatCount, mirror);
         }
     }
-    
+
     public enum Result {
         NORMAL,
         FREEZE_START,
@@ -130,7 +130,7 @@
      * return whether the specified time was within the range of key times
      * (NORMAL), was before the first key time (FREEZE_START) or after the last
      * key time (FREEZE_END). In any event, computed values are always returned.
-     * 
+     *
      * @param msec The time (in milliseconds) used to sample into the
      *        Interpolator. Based on the SystemClock.uptimeMillis() clock
      * @param values Where to write the computed values (may be NULL).
@@ -146,13 +146,13 @@
             default: return Result.FREEZE_END;
         }
     }
-    
+
     @Override
     protected void finalize() throws Throwable {
         nativeDestructor(native_instance);
         native_instance = 0;  // Other finalizers can still call us.
     }
-    
+
     private int mValueCount;
     private int mFrameCount;
     private long native_instance;
diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index 0aa6f12..fe73a1a 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -16,8 +16,8 @@
 
 // This file was generated from the C++ include file: SkColorFilter.h
 // Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, 
-// or one of the auxilary file specifications in device/tools/gluemaker.
+// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
+// or one of the auxiliary file specifications in device/tools/gluemaker.
 
 package android.graphics;
 
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 56d912b..0879371 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -63,7 +63,7 @@
      * @param colors       The sRGB colors to be distributed along the gradient line
      * @param positions    May be null. The relative positions [0..1] of
      *                     each corresponding color in the colors array. If this is null,
-     *                     the the colors are distributed evenly along the gradient line.
+     *                     the colors are distributed evenly along the gradient line.
      * @param tile         The Shader tiling mode
      */
     public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int[] colors,
@@ -82,7 +82,7 @@
      * @param colors       The colors to be distributed along the gradient line
      * @param positions    May be null. The relative positions [0..1] of
      *                     each corresponding color in the colors array. If this is null,
-     *                     the the colors are distributed evenly along the gradient line.
+     *                     the colors are distributed evenly along the gradient line.
      * @param tile         The Shader tiling mode
      *
      * @throws IllegalArgumentException if there are less than two colors, the colors do
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index fbb690c..748e9dd 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -232,7 +232,7 @@
     private static class NoImagePreloadHolder {
         public static final NativeAllocationRegistry sRegistry =
                 NativeAllocationRegistry.createMalloced(
-                Matrix.class.getClassLoader(), nGetNativeFinalizerWrapper());
+                Matrix.class.getClassLoader(), ExtraNatives.nGetNativeFinalizer());
     }
 
     private final long native_instance;
@@ -241,7 +241,7 @@
      * Create an identity matrix
      */
     public Matrix() {
-        native_instance = nCreateWrapper(0);
+        native_instance = ExtraNatives.nCreate(0);
         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
     }
 
@@ -251,7 +251,7 @@
      * @param src The matrix to copy into this matrix
      */
     public Matrix(Matrix src) {
-        native_instance = nCreateWrapper(src != null ? src.native_instance : 0);
+        native_instance = ExtraNatives.nCreate(src != null ? src.native_instance : 0);
         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
     }
 
@@ -562,7 +562,7 @@
 
     /**
      * Set the matrix to the scale and translate values that map the source rectangle to the
-     * destination rectangle, returning true if the the result can be represented.
+     * destination rectangle, returning true if the result can be represented.
      *
      * @param src the source rectangle to map from.
      * @param dst the destination rectangle to map to.
@@ -849,40 +849,6 @@
         return native_instance;
     }
 
-    /**
-     * Wrapper method we use to switch to ExtraNatives.nCreate(src) only on Ravenwood.
-     *
-     * @see ExtraNatives
-     */
-    @android.ravenwood.annotation.RavenwoodReplace
-    private static long nCreateWrapper(long src) {
-        return nCreate(src);
-    }
-
-    private static long nCreateWrapper$ravenwood(long src) {
-        return ExtraNatives.nCreate(src);
-    }
-
-    /**
-     * Wrapper method we use to switch to ExtraNatives.nGetNativeFinalizer(src) only on Ravenwood.
-     *
-     * @see ExtraNatives
-     */
-    @android.ravenwood.annotation.RavenwoodReplace
-    private static long nGetNativeFinalizerWrapper() {
-        return nGetNativeFinalizer();
-    }
-
-    private static long nGetNativeFinalizerWrapper$ravenwood() {
-        return ExtraNatives.nGetNativeFinalizer();
-    }
-
-    // ------------------ Regular JNI ------------------------
-
-    private static native long nCreate(long nSrc_or_zero);
-    private static native long nGetNativeFinalizer();
-
-
     // ------------------ Fast JNI ------------------------
 
     @FastNative
@@ -982,14 +948,6 @@
      * There are two methods that are called by the static initializers (either directly or
      * indirectly) in this class, namely nCreate() and nGetNativeFinalizer(). On Ravenwood
      * these methods can't be on the Matrix class itself, so we use a nested class to host them.
-     *
-     * We still keep the original nCreate() method and call it on non-ravenwood environment,
-     * in order to avoid problems in downstream (such as Android Studio).
-     *
-     * @see #nCreateWrapper(long)
-     * @see #nGetNativeFinalizerWrapper()
-     *
-     * TODO(b/337110712) Clean it up somehow. (remove the original nCreate() and unify the code?)
      */
     private static class ExtraNatives {
         static native long nCreate(long nSrc_or_zero);
diff --git a/graphics/java/android/graphics/Mesh.java b/graphics/java/android/graphics/Mesh.java
index a4bce9e..6be8332 100644
--- a/graphics/java/android/graphics/Mesh.java
+++ b/graphics/java/android/graphics/Mesh.java
@@ -271,7 +271,7 @@
      * not have a uniform with that name or if the uniform is declared with a type other than int
      * or int[1] then an IllegalArgumentException is thrown.
      *
-     * @param uniformName name matching the int uniform delcared in the shader program.
+     * @param uniformName name matching the int uniform declared in the shader program.
      * @param value       value corresponding to the int uniform with the given name.
      */
     public void setIntUniform(@NonNull String uniformName, int value) {
@@ -283,7 +283,7 @@
      * not have a uniform with that name or if the uniform is declared with a type other than ivec2
      * or int[2] then an IllegalArgumentException is thrown.
      *
-     * @param uniformName name matching the int uniform delcared in the shader program.
+     * @param uniformName name matching the int uniform declared in the shader program.
      * @param value1      first value corresponding to the int uniform with the given name.
      * @param value2      second value corresponding to the int uniform with the given name.
      */
@@ -296,7 +296,7 @@
      * not have a uniform with that name or if the uniform is declared with a type other than ivec3
      * or int[3] then an IllegalArgumentException is thrown.
      *
-     * @param uniformName name matching the int uniform delcared in the shader program.
+     * @param uniformName name matching the int uniform declared in the shader program.
      * @param value1      first value corresponding to the int uniform with the given name.
      * @param value2      second value corresponding to the int uniform with the given name.
      * @param value3      third value corresponding to the int uniform with the given name.
@@ -310,7 +310,7 @@
      * not have a uniform with that name or if the uniform is declared with a type other than ivec4
      * or int[4] then an IllegalArgumentException is thrown.
      *
-     * @param uniformName name matching the int uniform delcared in the shader program.
+     * @param uniformName name matching the int uniform declared in the shader program.
      * @param value1      first value corresponding to the int uniform with the given name.
      * @param value2      second value corresponding to the int uniform with the given name.
      * @param value3      third value corresponding to the int uniform with the given name.
@@ -327,7 +327,7 @@
      * int (for N=1), ivecN, or int[N], where N is the length of the values param, then an
      * IllegalArgumentException is thrown.
      *
-     * @param uniformName name matching the int uniform delcared in the shader program.
+     * @param uniformName name matching the int uniform declared in the shader program.
      * @param values      int values corresponding to the vec4 int uniform with the given name.
      */
     public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) {
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index af20957..382269f 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -22,12 +22,12 @@
  * The NinePatch class permits drawing a bitmap in nine or more sections.
  * Essentially, it allows the creation of custom graphics that will scale the
  * way that you define, when content added within the image exceeds the normal
- * bounds of the graphic. For a thorough explanation of a NinePatch image, 
- * read the discussion in the 
+ * bounds of the graphic. For a thorough explanation of a NinePatch image,
+ * read the discussion in the
  * <a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">2D
  * Graphics</a> document.
  * <p>
- * The <a href="{@docRoot}guide/developing/tools/draw9patch.html">Draw 9-Patch</a> 
+ * The <a href="{@docRoot}guide/developing/tools/draw9patch.html">Draw 9-Patch</a>
  * tool offers an extremely handy way to create your NinePatch images,
  * using a WYSIWYG graphics editor.
  * </p>
@@ -104,7 +104,7 @@
         this(bitmap, chunk, null);
     }
 
-    /** 
+    /**
      * Create a drawable projection from a bitmap to nine patches.
      *
      * @param bitmap The bitmap describing the patches.
@@ -122,7 +122,7 @@
     protected void finalize() throws Throwable {
         try {
             if (mNativeChunk != 0) {
-                // only attempt to destroy correctly initilized chunks
+                // only attempt to destroy correctly initialized chunks
                 nativeFinalize(mNativeChunk);
                 mNativeChunk = 0;
             }
@@ -169,8 +169,8 @@
     public Bitmap getBitmap() {
         return mBitmap;
     }
-    
-    /** 
+
+    /**
      * Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}.
      *
      * @param canvas A container for the current matrix and clip used to draw the NinePatch.
@@ -180,7 +180,7 @@
         canvas.drawPatch(this, location, mPaint);
     }
 
-    /** 
+    /**
      * Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}.
      *
      * @param canvas A container for the current matrix and clip used to draw the NinePatch.
@@ -190,7 +190,7 @@
         canvas.drawPatch(this, location, mPaint);
     }
 
-    /** 
+    /**
      * Draws the NinePatch. This method will ignore the paint returned
      * by {@link #getPaint()} and use the specified paint instead.
      *
diff --git a/graphics/java/android/graphics/PathMeasure.java b/graphics/java/android/graphics/PathMeasure.java
index 5500c52..2c6cfa5 100644
--- a/graphics/java/android/graphics/PathMeasure.java
+++ b/graphics/java/android/graphics/PathMeasure.java
@@ -25,14 +25,14 @@
      * setPath.
      *
      * Note that once a path is associated with the measure object, it is
-     * undefined if the path is subsequently modified and the the measure object
+     * undefined if the path is subsequently modified and the measure object
      * is used. If the path is modified, you must call setPath with the path.
      */
     public PathMeasure() {
         mPath = null;
         native_instance = native_create(0, false);
     }
-    
+
     /**
      * Create a PathMeasure object associated with the specified path object
      * (already created and specified). The measure object can now return the
@@ -40,7 +40,7 @@
      * path.
      *
      * Note that once a path is associated with the measure object, it is
-     * undefined if the path is subsequently modified and the the measure object
+     * undefined if the path is subsequently modified and the measure object
      * is used. If the path is modified, you must call setPath with the path.
      *
      * @param path The path that will be measured by this object
@@ -121,7 +121,7 @@
      * such as <code>dst.rLineTo(0, 0)</code>.</p>
      */
     public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) {
-        // Skia used to enforce this as part of it's API, but has since relaxed that restriction
+        // Skia used to enforce this as part of its API, but has since relaxed that restriction
         // so to maintain consistency in our API we enforce the preconditions here.
         float length = getLength();
         if (startD < 0) {
diff --git a/graphics/java/android/graphics/Rasterizer.java b/graphics/java/android/graphics/Rasterizer.java
index 29d82fa..5750954 100644
--- a/graphics/java/android/graphics/Rasterizer.java
+++ b/graphics/java/android/graphics/Rasterizer.java
@@ -16,8 +16,8 @@
 
 // This file was generated from the C++ include file: SkRasterizer.h
 // Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, 
-// or one of the auxilary file specifications in device/tools/gluemaker.
+// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
+// or one of the auxiliary file specifications in device/tools/gluemaker.
 
 package android.graphics;
 
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 411a10b..5211e3a 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -165,7 +165,7 @@
     public String toShortString() {
         return toShortString(new StringBuilder(32));
     }
-    
+
     /**
      * Return a string representation of the rectangle in a compact form.
      * @hide
@@ -184,7 +184,7 @@
      *
      * <p>You can later recover the Rect from this string through
      * {@link #unflattenFromString(String)}.
-     * 
+     *
      * @return Returns a new String of the form "left top right bottom"
      */
     @NonNull
@@ -314,7 +314,7 @@
     public final int height() {
         return bottom - top;
     }
-    
+
     /**
      * @return the horizontal center of the rectangle. If the computed value
      *         is fractional, this method returns the largest integer that is
@@ -323,7 +323,7 @@
     public final int centerX() {
         return (left + right) >> 1;
     }
-    
+
     /**
      * @return the vertical center of the rectangle. If the computed value
      *         is fractional, this method returns the largest integer that is
@@ -332,14 +332,14 @@
     public final int centerY() {
         return (top + bottom) >> 1;
     }
-    
+
     /**
      * @return the exact horizontal center of the rectangle as a float.
      */
     public final float exactCenterX() {
         return (left + right) * 0.5f;
     }
-    
+
     /**
      * @return the exact vertical center of the rectangle as a float.
      */
@@ -493,7 +493,7 @@
      * @param top The top of the rectangle being tested for containment
      * @param right The right side of the rectangle being tested for containment
      * @param bottom The bottom of the rectangle being tested for containment
-     * @return true iff the the 4 specified sides of a rectangle are inside or
+     * @return true iff the 4 specified sides of a rectangle are inside or
      *              equal to this rectangle
      */
     public boolean contains(int left, int top, int right, int bottom) {
@@ -548,7 +548,7 @@
         }
         return false;
     }
-    
+
     /**
      * If the specified rectangle intersects this rectangle, return true and set
      * this rectangle to that intersection, otherwise return false and do not
@@ -670,7 +670,7 @@
     public void union(@NonNull Rect r) {
         union(r.left, r.top, r.right, r.bottom);
     }
-    
+
     /**
      * Update this Rect to enclose itself and the [x,y] coordinate. There is no
      * check to see that this rectangle is non-empty.
diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java
index ff50a0c..5b9b764 100644
--- a/graphics/java/android/graphics/RectF.java
+++ b/graphics/java/android/graphics/RectF.java
@@ -38,7 +38,7 @@
     public float top;
     public float right;
     public float bottom;
-    
+
     /**
      * Create a new empty RectF. All coordinates are initialized to 0.
      */
@@ -78,7 +78,7 @@
             bottom = r.bottom;
         }
     }
-    
+
     public RectF(@Nullable Rect r) {
         if (r == null) {
             left = top = right = bottom = 0.0f;
@@ -121,7 +121,7 @@
     public String toShortString() {
         return toShortString(new StringBuilder(32));
     }
-    
+
     /**
      * Return a string representation of the rectangle in a compact form.
      * @hide
@@ -134,7 +134,7 @@
         sb.append(','); sb.append(bottom); sb.append(']');
         return sb.toString();
     }
-    
+
     /**
      * Print short representation to given writer.
      * @hide
@@ -183,14 +183,14 @@
     public final float centerY() {
         return (top + bottom) * 0.5f;
     }
-    
+
     /**
      * Set the rectangle to (0,0,0,0)
      */
     public void setEmpty() {
         left = right = top = bottom = 0;
     }
-    
+
     /**
      * Set the rectangle's coordinates to the specified values. Note: no range
      * checking is performed, so it is up to the caller to ensure that
@@ -220,7 +220,7 @@
         this.right  = src.right;
         this.bottom = src.bottom;
     }
-    
+
     /**
      * Copy the coordinates from src into this rectangle.
      *
@@ -261,7 +261,7 @@
         left = newLeft;
         top = newTop;
     }
-    
+
     /**
      * Inset the rectangle by (dx,dy). If dx is positive, then the sides are
      * moved inwards, making the rectangle narrower. If dx is negative, then the
@@ -293,7 +293,7 @@
         return left < right && top < bottom  // check for empty first
                 && x >= left && x < right && y >= top && y < bottom;
     }
-    
+
     /**
      * Returns true iff the 4 specified sides of a rectangle are inside or equal
      * to this rectangle. i.e. is this rectangle a superset of the specified
@@ -303,7 +303,7 @@
      * @param top The top of the rectangle being tested for containment
      * @param right The right side of the rectangle being tested for containment
      * @param bottom The bottom of the rectangle being tested for containment
-     * @return true iff the the 4 specified sides of a rectangle are inside or
+     * @return true iff the 4 specified sides of a rectangle are inside or
      *              equal to this rectangle
      */
     public boolean contains(float left, float top, float right, float bottom) {
@@ -313,7 +313,7 @@
                 && this.left <= left && this.top <= top
                 && this.right >= right && this.bottom >= bottom;
     }
-    
+
     /**
      * Returns true iff the specified rectangle r is inside or equal to this
      * rectangle. An empty rectangle never contains another rectangle.
@@ -329,7 +329,7 @@
                 && left <= r.left && top <= r.top
                 && right >= r.right && bottom >= r.bottom;
     }
-    
+
     /**
      * If the rectangle specified by left,top,right,bottom intersects this
      * rectangle, return true and set this rectangle to that intersection,
@@ -367,7 +367,7 @@
         }
         return false;
     }
-    
+
     /**
      * If the specified rectangle intersects this rectangle, return true and set
      * this rectangle to that intersection, otherwise return false and do not
@@ -382,7 +382,7 @@
     public boolean intersect(@NonNull RectF r) {
         return intersect(r.left, r.top, r.right, r.bottom);
     }
-    
+
     /**
      * If rectangles a and b intersect, return true and set this rectangle to
      * that intersection, otherwise return false and do not change this
@@ -406,7 +406,7 @@
         }
         return false;
     }
-    
+
     /**
      * Returns true if this rectangle intersects the specified rectangle.
      * In no event is this rectangle modified. No check is performed to see
@@ -426,7 +426,7 @@
         return this.left < right && left < this.right
                 && this.top < bottom && top < this.bottom;
     }
-    
+
     /**
      * Returns true iff the two specified rectangles intersect. In no event are
      * either of the rectangles modified. To record the intersection,
@@ -441,7 +441,7 @@
         return a.left < b.right && b.left < a.right
                 && a.top < b.bottom && b.top < a.bottom;
     }
-    
+
     /**
      * Set the dst integer Rect by rounding this rectangle's coordinates
      * to their nearest integer values.
@@ -489,7 +489,7 @@
             }
         }
     }
-    
+
     /**
      * Update this Rect to enclose itself and the specified rectangle. If the
      * specified rectangle is empty, nothing is done. If this rectangle is empty
@@ -500,7 +500,7 @@
     public void union(@NonNull RectF r) {
         union(r.left, r.top, r.right, r.bottom);
     }
-    
+
     /**
      * Update this Rect to enclose itself and the [x,y] coordinate. There is no
      * check to see that this rectangle is non-empty.
@@ -520,7 +520,7 @@
             bottom = y;
         }
     }
-    
+
     /**
      * Swap top/bottom or left/right if there are flipped (i.e. left > right
      * and/or top > bottom). This can be called if
@@ -548,7 +548,7 @@
     public int describeContents() {
         return 0;
     }
-    
+
     /**
      * Write this rectangle to the specified parcel. To restore a rectangle from
      * a parcel, use readFromParcel()
@@ -561,7 +561,7 @@
         out.writeFloat(right);
         out.writeFloat(bottom);
     }
-    
+
     public static final @android.annotation.NonNull Parcelable.Creator<RectF> CREATOR = new Parcelable.Creator<RectF>() {
         /**
          * Return a new rectangle from the data in the specified parcel.
@@ -572,7 +572,7 @@
             r.readFromParcel(in);
             return r;
         }
-        
+
         /**
          * Return an array of rectangles of the specified size.
          */
@@ -581,7 +581,7 @@
             return new RectF[size];
         }
     };
-    
+
     /**
      * Set the rectangle's coordinates from the data stored in the specified
      * parcel. To write a rectangle to a parcel, call writeToParcel().
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 2732569..0650b78 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -765,12 +765,12 @@
      * Default value is false. See
      * {@link #setProjectBackwards(boolean)} for a description of what this entails.
      *
-     * @param shouldRecieve True if this RenderNode is a projection receiver, false otherwise.
+     * @param shouldReceive True if this RenderNode is a projection receiver, false otherwise.
      *                      Default is false.
      * @return True if the value changed, false if the new value was the same as the previous value.
      */
-    public boolean setProjectionReceiver(boolean shouldRecieve) {
-        return nSetProjectionReceiver(mNativeRenderNode, shouldRecieve);
+    public boolean setProjectionReceiver(boolean shouldReceive) {
+        return nSetProjectionReceiver(mNativeRenderNode, shouldReceive);
     }
 
     /**
@@ -1799,7 +1799,7 @@
     private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject);
 
     @CriticalNative
-    private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve);
+    private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldReceive);
 
     @CriticalNative
     private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top,
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 50b167e..3256f31 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -318,7 +318,7 @@
     }
 
     /**
-     * Releases the the texture content. This is needed in single buffered mode to allow the image
+     * Releases the texture content. This is needed in single buffered mode to allow the image
      * content producer to take ownership of the image buffer.
      * <p>
      * For more information see {@link #SurfaceTexture(int, boolean)}.
@@ -431,7 +431,7 @@
      * error.
      * <p>
      * Note that while calling this method causes all the buffers to be freed
-     * from the perspective of the the SurfaceTexture, if there are additional
+     * from the perspective of the SurfaceTexture, if there are additional
      * references on the buffers (e.g. if a buffer is referenced by a client or
      * by OpenGL ES as a texture) then those buffer will remain allocated.
      * <p>
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 4c4e8fa..fd78816 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -600,7 +600,7 @@
          * {@link #setWeight} and {@link #setItalic}.
          *
          * If {@link #setWeight} is not called, the fallback family keeps the default weight.
-         * Similary, if {@link #setItalic} is not called, the fallback family keeps the default
+         * Similarly, if {@link #setItalic} is not called, the fallback family keeps the default
          * italic information. For example, calling {@code builder.setFallback("sans-serif-light")}
          * is equivalent to calling {@code builder.setFallback("sans-serif").setWeight(300)} in
          * terms of fallback. The default weight and italic information are overridden by calling
@@ -794,7 +794,7 @@
         /**
          * Returns the maximum capacity of custom fallback families.
          *
-         * This includes the the first font family passed to the constructor.
+         * This includes the first font family passed to the constructor.
          * It is guaranteed that the value will be greater than or equal to 64.
          *
          * @return the maximum number of font families for the custom fallback
@@ -816,7 +816,7 @@
         /**
          * Sets a system fallback by name.
          *
-         * You can specify generic font familiy names or OEM specific family names. If the system
+         * You can specify generic font family names or OEM specific family names. If the system
          * don't have a specified fallback, the default fallback is used instead.
          * For more information about generic font families, see <a
          * href="https://www.w3.org/TR/css-fonts-4/#generic-font-families">CSS specification</a>
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index 81769e2..6bb22a1 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -16,8 +16,8 @@
 
 // This file was generated from the C++ include file: SkXfermode.h
 // Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, 
-// or one of the auxilary file specifications in device/tools/gluemaker.
+// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
+// or one of the auxiliary file specifications in device/tools/gluemaker.
 
 package android.graphics;
 
@@ -28,7 +28,7 @@
  * Xfermode is the base class for objects that are called to implement custom
  * "transfer-modes" in the drawing pipeline. The static function Create(Modes)
  * can be called to return an instance of any of the predefined subclasses as
- * specified in the Modes enum. When an Xfermode is assigned to an Paint, then
+ * specified in the Modes enum. When an Xfermode is assigned to a Paint, then
  * objects drawn with that paint have the xfermode applied.
  */
 public class Xfermode {
diff --git a/graphics/java/android/graphics/YuvImage.java b/graphics/java/android/graphics/YuvImage.java
index ce35b55..b0c7f20 100644
--- a/graphics/java/android/graphics/YuvImage.java
+++ b/graphics/java/android/graphics/YuvImage.java
@@ -63,7 +63,7 @@
     private int mWidth;
 
     /**
-     * The height of the the image.
+     * The height of the image.
      */
     private int mHeight;
 
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 688425a..7ee7d6b 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -98,7 +98,7 @@
      * extra content to reveal within the clip path when performing affine transformations on the
      * layers.
      *
-     * Each layers will reserve 25% of it's width and height.
+     * Each layers will reserve 25% of its width and height.
      *
      * As a result, the view port of the layers is smaller than their intrinsic width and height.
      */
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 4972e92..7f2feac 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -839,7 +839,7 @@
     }
 
     /**
-     * Describes the current state, as a union of primitve states, such as
+     * Describes the current state, as a union of primitive states, such as
      * {@link android.R.attr#state_focused},
      * {@link android.R.attr#state_selected}, etc.
      * Some drawables may modify their imagery based on the selected state.
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 166a795..29d033e 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -936,7 +936,7 @@
     }
 
     /**
-     * Retrn the inner radius of the ring
+     * Return the inner radius of the ring
      *
      * @see #setInnerRadius(int)
      * @attr ref android.R.styleable#GradientDrawable_innerRadius
diff --git a/graphics/java/android/graphics/drawable/shapes/PathShape.java b/graphics/java/android/graphics/drawable/shapes/PathShape.java
index 393fdee..299f6d5 100644
--- a/graphics/java/android/graphics/drawable/shapes/PathShape.java
+++ b/graphics/java/android/graphics/drawable/shapes/PathShape.java
@@ -93,7 +93,7 @@
             && Float.compare(pathShape.mStdHeight, mStdHeight) == 0
             && Float.compare(pathShape.mScaleX, mScaleX) == 0
             && Float.compare(pathShape.mScaleY, mScaleY) == 0
-            // Path does not have equals implementation but incase it gains one, use it here
+            // Path does not have equals implementation but in case it gains one, use it here
             && Objects.equals(mPath, pathShape.mPath);
     }
 
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 318aadd..2893177 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -789,7 +789,7 @@
             return false;
         }
 
-        // ByteBuffer#equals compares all bytes which is not performant for e.g HashMap. Since
+        // ByteBuffer#equals compares all bytes which is not performant for e.g. HashMap. Since
         // underlying native font object holds buffer address, check if this buffer points exactly
         // the same address as a shortcut of equality. For being compatible with of API30 or before,
         // check buffer position even if the buffer points the same address.
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
index ff38282..abcafb6 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -34,7 +34,7 @@
  */
 public class FontFileUtil {
 
-    private FontFileUtil() {}  // Do not instanciate
+    private FontFileUtil() {}  // Do not instantiate
 
     /**
      * Unpack the weight value from packed integer.
@@ -87,7 +87,7 @@
         }
 
         if (weight != -1 && italic != -1) {
-            // Both weight/italic style are specifeid by variation settings.
+            // Both weight/italic style are specified by variation settings.
             // No need to look into OS/2 table.
             // TODO: Good to look HVAR table to check if this font supports wght/ital axes.
             return pack(weight, italic == 1);
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index a90961e..f727f5b 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -216,7 +216,7 @@
                 } else if (defaultFamily != null) {
                     familyListSet.familyList.add(defaultFamily);
                 } else {
-                    // There is no valid for for default fallback. Ignore.
+                    // There is no valid for default fallback. Ignore.
                 }
             }
         }
diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
index 0c6d4bd..d8cf21e 100644
--- a/graphics/java/android/graphics/text/LineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -376,8 +376,8 @@
      * @see LineBreaker#computeLineBreaks
      */
     public static class Result {
-        // Following two contstant must be synced with minikin's line breaker.
-        // TODO(nona): Remove these constatns by introducing native methods.
+        // Following two constants must be synced with minikin's line breaker.
+        // TODO(nona): Remove these constants by introducing native methods.
         private static final int TAB_MASK = 0x20000000;
         private static final int HYPHEN_MASK = 0xFF;
         private static final int START_HYPHEN_MASK = 0x18;  // 0b11000
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index f8328b1..671eb6e 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -139,7 +139,7 @@
      * Returns the glyph ID used for drawing the glyph at the given index.
      *
      * @param index the glyph index
-     * @return An glyph ID of the font.
+     * @return A glyph ID of the font.
      */
     @IntRange(from = 0)
     public int getGlyphId(@IntRange(from = 0) int index) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index e93b0bf..1fbaeea 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -256,8 +256,10 @@
     static Color getContainerBackgroundColor(
             @NonNull TaskFragmentContainer container, @NonNull Color defaultColor) {
         final Activity activity = container.getTopNonFinishingActivity();
-        if (activity == null || !activity.isResumed()) {
-            // This can happen when the top activity in the container is from a different process.
+        if (activity == null) {
+            // This can happen when the activities in the container are from a different process.
+            // TODO(b/340984203) Report whether the top activity is in the same process. Use default
+            // color if not.
             return defaultColor;
         }
 
@@ -515,8 +517,11 @@
     private void onStartDragging() {
         mRenderer.mIsDragging = true;
         mRenderer.mDragHandle.setPressed(mRenderer.mIsDragging);
+        mRenderer.updateSurface();
+
+        // Veil visibility change should be applied together with the surface boost transaction in
+        // the wct.
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        mRenderer.updateSurface(t);
         mRenderer.showVeils(t);
 
         // Callbacks must be executed on the executor to release mLock and prevent deadlocks.
@@ -532,18 +537,18 @@
 
     @GuardedBy("mLock")
     private void onDrag() {
-        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        mRenderer.updateSurface(t);
-        t.apply();
+        mRenderer.updateSurface();
     }
 
     @GuardedBy("mLock")
     private void onFinishDragging() {
         mDividerPosition = adjustDividerPositionForSnapPoints(mDividerPosition);
         mRenderer.setDividerPosition(mDividerPosition);
+        mRenderer.updateSurface();
 
+        // Veil visibility change should be applied together with the surface boost transaction in
+        // the wct.
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        mRenderer.updateSurface(t);
         mRenderer.hideVeils(t);
 
         // Callbacks must be executed on the executor to release mLock and prevent deadlocks.
@@ -994,6 +999,22 @@
          * Updates the positions and crops of the divider surface and veil surfaces. This method
          * should be called when {@link #mProperties} is changed or while dragging to update the
          * position of the divider surface and the veil surfaces.
+         *
+         * This method applies the changes in a stand-alone surface transaction immediately.
+         */
+        private void updateSurface() {
+            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            updateSurface(t);
+            t.apply();
+        }
+
+        /**
+         * Updates the positions and crops of the divider surface and veil surfaces. This method
+         * should be called when {@link #mProperties} is changed or while dragging to update the
+         * position of the divider surface and the veil surfaces.
+         *
+         * This method applies the changes in the provided surface transaction and can be synced
+         * with other changes.
          */
         private void updateSurface(@NonNull SurfaceControl.Transaction t) {
             final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index a23a4741..f9a6caf 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -21,6 +21,7 @@
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED;
 
 import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
 import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
@@ -358,6 +359,13 @@
         wct.addTaskFragmentOperation(fragmentToken, operation);
     }
 
+    void setTaskFragmentPinned(@NonNull WindowContainerTransaction wct,
+            @NonNull IBinder fragmentToken, boolean pinned) {
+        final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+                OP_TYPE_SET_PINNED).setBooleanValue(pinned).build();
+        wct.addTaskFragmentOperation(fragmentToken, operation);
+    }
+
     void setTaskFragmentDimOnTask(@NonNull WindowContainerTransaction wct,
             @NonNull IBinder fragmentToken, boolean dimOnTask) {
         final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 5b0e6b9..13c2d1f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -350,8 +350,7 @@
             // Resets the isolated navigation and updates the container.
             final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
             final WindowContainerTransaction wct = transactionRecord.getTransaction();
-            mPresenter.setTaskFragmentIsolatedNavigation(wct, containerToUnpin,
-                    false /* isolated */);
+            mPresenter.setTaskFragmentPinned(wct, containerToUnpin, false /* pinned */);
             updateContainer(wct, containerToUnpin);
             transactionRecord.apply(false /* shouldApplyIndependently */);
             updateCallbackIfNecessary();
@@ -1078,8 +1077,7 @@
             return true;
         }
 
-        // Skip resolving if the activity is on an isolated navigated TaskFragmentContainer.
-        if (container != null && container.isIsolatedNavigationEnabled()) {
+        if (container != null && container.shouldSkipActivityResolving()) {
             return true;
         }
 
@@ -1535,8 +1533,7 @@
             final TaskFragmentContainer taskFragmentContainer = getContainerWithActivity(
                     launchingActivity);
             if (taskFragmentContainer != null
-                    && taskFragmentContainer.isIsolatedNavigationEnabled()) {
-                // Skip resolving if started from an isolated navigated TaskFragmentContainer.
+                    && taskFragmentContainer.shouldSkipActivityResolving()) {
                 return null;
             }
             if (isAssociatedWithOverlay(launchingActivity)) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 0e4fb30..2704813 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -401,18 +401,26 @@
             return;
         }
 
-        setTaskFragmentIsolatedNavigation(wct, secondaryContainer, !isStacked /* isolatedNav */);
+        setTaskFragmentPinned(wct, secondaryContainer, !isStacked /* pinned */);
         if (isStacked && !splitPinRule.isSticky()) {
             secondaryContainer.getTaskContainer().removeSplitPinContainer();
         }
     }
 
     /**
-     * Sets whether to enable isolated navigation for this {@link TaskFragmentContainer}
+     * Sets whether to enable isolated navigation for this {@link TaskFragmentContainer}.
+     * <p>
+     * If a container enables isolated navigation, activities can't be launched to this container
+     * unless explicitly requested to be launched to.
+     *
+     * @see TaskFragmentContainer#isOverlayWithActivityAssociation()
      */
     void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct,
                                            @NonNull TaskFragmentContainer container,
                                            boolean isolatedNavigationEnabled) {
+        if (!Flags.activityEmbeddingOverlayPresentationFlag() && container.isOverlay()) {
+            return;
+        }
         if (container.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
             return;
         }
@@ -422,6 +430,28 @@
     }
 
     /**
+     * Sets whether to pin this {@link TaskFragmentContainer}.
+     * <p>
+     * If a container is pinned, it won't be chosen as the launch target unless it's the launching
+     * container.
+     *
+     * @see TaskFragmentContainer#isAlwaysOnTopOverlay()
+     * @see TaskContainer#getSplitPinContainer()
+     */
+    void setTaskFragmentPinned(@NonNull WindowContainerTransaction wct,
+                               @NonNull TaskFragmentContainer container,
+                               boolean pinned) {
+        if (!Flags.activityEmbeddingOverlayPresentationFlag() && container.isOverlay()) {
+            return;
+        }
+        if (container.isPinned() == pinned) {
+            return;
+        }
+        container.setPinned(pinned);
+        setTaskFragmentPinned(wct, container.getTaskFragmentToken(), pinned);
+    }
+
+    /**
      * Resizes the task fragment if it was already registered. Skips the operation if the container
      * creation has not been reported from the server yet.
      */
@@ -586,6 +616,11 @@
         super.setCompanionTaskFragment(wct, primary, secondary);
     }
 
+    /**
+     * Applies the {@code attributes} to a standalone {@code container}.
+     *
+     * @param minDimensions the minimum dimension of the container.
+     */
     void applyActivityStackAttributes(
             @NonNull WindowContainerTransaction wct,
             @NonNull TaskFragmentContainer container,
@@ -594,16 +629,17 @@
         final Rect relativeBounds = sanitizeBounds(attributes.getRelativeBounds(), minDimensions,
                 container);
         final boolean isFillParent = relativeBounds.isEmpty();
-        // Note that we only set isolated navigation for overlay container without activity
-        // association. Activity will be launched to an expanded container on top of the overlay
-        // if the overlay is associated with an activity. Thus, an overlay with activity association
-        // will never be isolated navigated.
-        final boolean isIsolatedNavigated = container.isAlwaysOnTopOverlay() && !isFillParent;
         final boolean dimOnTask = !isFillParent
-                && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK
-                && Flags.fullscreenDimFlag();
+                && Flags.fullscreenDimFlag()
+                && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK;
         final IBinder fragmentToken = container.getTaskFragmentToken();
 
+        if (container.isAlwaysOnTopOverlay()) {
+            setTaskFragmentPinned(wct, container, !isFillParent);
+        } else if (container.isOverlayWithActivityAssociation()) {
+            setTaskFragmentIsolatedNavigation(wct, container, !isFillParent);
+        }
+
         // TODO(b/243518738): Update to resizeTaskFragment after we migrate WCT#setRelativeBounds
         //  and WCT#setWindowingMode to take fragmentToken.
         resizeTaskFragmentIfRegistered(wct, container, relativeBounds);
@@ -612,7 +648,6 @@
         updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
         // Always use default animation for standalone ActivityStack.
         updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT);
-        setTaskFragmentIsolatedNavigation(wct, container, isIsolatedNavigated);
         setTaskFragmentDimOnTask(wct, fragmentToken, dimOnTask);
     }
 
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 c952dfe..4825543 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -184,6 +184,11 @@
     private boolean mIsIsolatedNavigationEnabled;
 
     /**
+     * Whether this TaskFragment is pinned.
+     */
+    private boolean mIsPinned;
+
+    /**
      * Whether to apply dimming on the parent Task that was requested last.
      */
     private boolean mLastDimOnTask;
@@ -893,6 +898,34 @@
         mIsIsolatedNavigationEnabled = isolatedNavigationEnabled;
     }
 
+    /**
+     * Returns whether this container is pinned.
+     *
+     * @see android.window.TaskFragmentOperation#OP_TYPE_SET_PINNED
+     */
+    boolean isPinned() {
+        return mIsPinned;
+    }
+
+    /**
+     * Sets whether to pin this container or not.
+     *
+     * @see #isPinned()
+     */
+    void setPinned(boolean pinned) {
+        mIsPinned = pinned;
+    }
+
+    /**
+     * Indicates to skip activity resolving if the activity is from this container.
+     *
+     * @see #isIsolatedNavigationEnabled()
+     * @see #isPinned()
+     */
+    boolean shouldSkipActivityResolving() {
+        return isIsolatedNavigationEnabled() || isPinned();
+    }
+
     /** Sets whether to apply dim on the parent Task. */
     void setLastDimOnTask(boolean lastDimOnTask) {
         mLastDimOnTask = lastDimOnTask;
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
index ad913c9..b0a45e2 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -631,15 +631,8 @@
         assertEquals(defaultColor,
                 DividerPresenter.getContainerBackgroundColor(container, defaultColor));
 
-        // When the top non-finishing activity is not resumed, the default color should be returned.
+        // When the top non-finishing activity is non-null, its background color should be returned.
         when(container.getTopNonFinishingActivity()).thenReturn(activity);
-        when(activity.isResumed()).thenReturn(false);
-        assertEquals(defaultColor,
-                DividerPresenter.getContainerBackgroundColor(container, defaultColor));
-
-        // When the top non-finishing activity is resumed, its background color should be returned.
-        when(container.getTopNonFinishingActivity()).thenReturn(activity);
-        when(activity.isResumed()).thenReturn(true);
         assertEquals(activityBackgroundColor,
                 DividerPresenter.getContainerBackgroundColor(container, defaultColor));
     }
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 049a9e2..9ebcb759 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -188,6 +188,32 @@
     }
 
     @Test
+    public void testSetIsolatedNavigation_overlayFeatureDisabled_earlyReturn() {
+        mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
+
+        final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
+
+        mSplitPresenter.setTaskFragmentIsolatedNavigation(mTransaction, container,
+                !container.isIsolatedNavigationEnabled());
+
+        verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(),
+                any(IBinder.class), anyBoolean());
+    }
+
+    @Test
+    public void testSetPinned_overlayFeatureDisabled_earlyReturn() {
+        mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
+
+        final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
+
+        mSplitPresenter.setTaskFragmentPinned(mTransaction, container,
+                !container.isPinned());
+
+        verify(mSplitPresenter, never()).setTaskFragmentPinned(any(), any(IBinder.class),
+                anyBoolean());
+    }
+
+    @Test
     public void testGetAllNonFinishingOverlayContainers() {
         assertThat(mSplitController.getAllNonFinishingOverlayContainers()).isEmpty();
 
@@ -608,8 +634,11 @@
                 WINDOWING_MODE_UNDEFINED);
         verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
                 TaskFragmentAnimationParams.DEFAULT);
-        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false);
         verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, false);
+        verify(mSplitPresenter, never()).setTaskFragmentPinned(any(),
+                any(TaskFragmentContainer.class), anyBoolean());
+        verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(),
+                any(TaskFragmentContainer.class), anyBoolean());
     }
 
     @Test
@@ -630,9 +659,9 @@
                 WINDOWING_MODE_MULTI_WINDOW);
         verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
                 TaskFragmentAnimationParams.DEFAULT);
-        // Set isolated navigation to false if the overlay container is associated with
-        // the launching activity.
-        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false);
+        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, true);
+        verify(mSplitPresenter, never()).setTaskFragmentPinned(any(),
+                any(TaskFragmentContainer.class), anyBoolean());
         verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, true);
     }
 
@@ -655,10 +684,9 @@
                 container, WINDOWING_MODE_MULTI_WINDOW);
         verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
                 TaskFragmentAnimationParams.DEFAULT);
-        // Set isolated navigation to false if the overlay container is associated with
-        // the launching activity.
-        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction,
-                container, true);
+        verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(),
+                any(TaskFragmentContainer.class), anyBoolean());
+        verify(mSplitPresenter).setTaskFragmentPinned(mTransaction, container, true);
         verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, true);
     }
 
@@ -678,6 +706,8 @@
         verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
                 TaskFragmentAnimationParams.DEFAULT);
         verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false);
+        verify(mSplitPresenter, never()).setTaskFragmentPinned(any(),
+                any(TaskFragmentContainer.class), anyBoolean());
         verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, false);
     }
 
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index c677484..3fbce9ec 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED;
 
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_PRIMARY_WITH_SECONDARY;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_SECONDARY_WITH_PRIMARY;
@@ -285,6 +286,28 @@
     }
 
     @Test
+    public void testSetTaskFragmentPinned() {
+        final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+
+        // Verify the default.
+        assertFalse(container.isPinned());
+
+        mPresenter.setTaskFragmentPinned(mTransaction, container, true);
+
+        final TaskFragmentOperation expectedOperation = new TaskFragmentOperation.Builder(
+                OP_TYPE_SET_PINNED).setBooleanValue(true).build();
+        verify(mTransaction).addTaskFragmentOperation(container.getTaskFragmentToken(),
+                expectedOperation);
+        assertTrue(container.isPinned());
+
+        // No request to set the same animation params.
+        clearInvocations(mTransaction);
+        mPresenter.setTaskFragmentPinned(mTransaction, container, true);
+
+        verify(mTransaction, never()).addTaskFragmentOperation(any(), any());
+    }
+
+    @Test
     public void testGetMinDimensionsForIntent() {
         final Intent intent = new Intent(ApplicationProvider.getApplicationContext(),
                 MinimumDimensionActivity.class);
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index fe68123..8977d5c 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -71,3 +71,10 @@
     description: "Hides the bubble overflow if there aren't any overflowed bubbles"
     bug: "334175587"
 }
+
+flag {
+    name: "enable_retrievable_bubbles"
+    namespace: "multitasking"
+    description: "Allow opening bubbles overflow UI without bubbles being visible"
+    bug: "340337839"
+}
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 3aecf5f..3492f13 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -62,9 +62,9 @@
     <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
     <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> et <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> autres"</string>
     <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Déplacer dans coin sup. gauche"</string>
-    <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer dans coin sup. droit"</string>
-    <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer dans coin inf. gauche"</string>
-    <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer dans coin inf. droit"</string>
+    <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer en haut à droite"</string>
+    <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer en bas à gauche"</string>
+    <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer en bas à droite"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"développer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"réduire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 7b7779d..302c007 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -71,7 +71,7 @@
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Калкып чыкма билдирмени жабуу"</string>
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Жазышууда калкып чыкма билдирмелер көрүнбөсүн"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
-    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн таптап коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
+    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн тийип коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string>
     <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн \"Башкарууну\" басыңыз"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түшүндүм"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 4a9fab9..5e43506 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -53,7 +53,7 @@
     <string name="accessibility_split_top" msgid="2789329702027147146">"Дээд талд хуваах"</string>
     <string name="accessibility_split_bottom" msgid="8694551025220868191">"Доод талд хуваах"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Нэг гарын горимыг ашиглаж байна"</string>
-    <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Гарахын тулд дэлгэцийн доод хэсгээс дээш шударч эсвэл апп дээр хүссэн газраа товшино уу"</string>
+    <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Гарахын тулд дэлгэцийн доод хэсгээс дээш шударч эсвэл аппын дээр хүссэн газраа товшино уу"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Нэг гарын горимыг эхлүүлэх"</string>
     <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Нэг гарын горимоос гарах"</string>
     <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н бөмбөлгүүдийн тохиргоо"</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
similarity index 88%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
index b41454d..bdd89c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.desktopmode;
+package com.android.wm.shell.shared;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -41,15 +41,6 @@
     public static final boolean IS_DISPLAY_CHANGE_ENABLED = SystemProperties.getBoolean(
             "persist.wm.debug.desktop_change_display", false);
 
-
-    /**
-     * Flag to indicate that desktop stashing is enabled.
-     * When enabled, swiping home from desktop stashes the open apps. Next app that launches,
-     * will be added to the desktop.
-     */
-    private static final boolean IS_STASHING_ENABLED = SystemProperties.getBoolean(
-            "persist.wm.debug.desktop_stashing", false);
-
     /**
      * Flag to indicate whether to apply shadows to windows in desktop mode.
      */
@@ -109,14 +100,6 @@
     }
 
     /**
-     * Return {@code true} if desktop task stashing is enabled when going home.
-     * Allows users to use home screen to add tasks to desktop.
-     */
-    public static boolean isStashingEnabled() {
-        return IS_STASHING_ENABLED;
-    }
-
-    /**
      * Return whether to use window shadows.
      *
      * @param isFocusedWindow whether the window to apply shadows to is focused
@@ -144,7 +127,7 @@
     /**
      * Return the maximum limit on the number of Tasks to show in Desktop Mode at any one time.
      */
-    static int getMaxTaskLimit() {
+    public static int getMaxTaskLimit() {
         return MAX_TASK_LIMIT;
     }
 
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
index dcd4062..785e30d 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
@@ -69,8 +69,12 @@
 
     /** Returns {@code true} if the transition is opening or closing mode. */
     public static boolean isOpenOrCloseMode(@TransitionInfo.TransitionMode int mode) {
-        return mode == TRANSIT_OPEN || mode == TRANSIT_CLOSE
-                || mode == TRANSIT_TO_FRONT || mode == TRANSIT_TO_BACK;
+        return isOpeningMode(mode) || mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK;
+    }
+
+    /** Returns {@code true} if the transition is opening mode. */
+    public static boolean isOpeningMode(@TransitionInfo.TransitionMode int mode) {
+        return mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT;
     }
 
     /** Returns {@code true} if the transition has a display change. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 163a896..5600664 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -646,7 +646,7 @@
 
     private void tryDispatchOnBackCancelled(IOnBackInvokedCallback callback) {
         if (!mOnBackStartDispatched) {
-            Log.e(TAG, "Skipping dispatching onBackCancelled. Start was never dispatched.");
+            Log.d(TAG, "Skipping dispatching onBackCancelled. Start was never dispatched.");
             return;
         }
         if (callback == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index 037b1ec..c988c2f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.wm.shell.back
 
 import android.animation.Animator
@@ -34,6 +35,7 @@
 import android.view.SurfaceControl
 import android.view.animation.DecelerateInterpolator
 import android.view.animation.Interpolator
+import android.view.animation.Transformation
 import android.window.BackEvent
 import android.window.BackMotionEvent
 import android.window.BackNavigationInfo
@@ -46,52 +48,45 @@
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.animation.Interpolators
 import com.android.wm.shell.protolog.ShellProtoLogGroup
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import javax.inject.Inject
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.min
 
-/** Class that defines cross-activity animation.  */
-@ShellMainThread
-class CrossActivityBackAnimation @Inject constructor(
+abstract class CrossActivityBackAnimation(
     private val context: Context,
     private val background: BackAnimationBackground,
-    private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+    private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+    protected val transaction: SurfaceControl.Transaction,
+    private val choreographer: Choreographer
 ) : ShellBackAnimation() {
 
-    private val startClosingRect = RectF()
-    private val targetClosingRect = RectF()
-    private val currentClosingRect = RectF()
+    protected val startClosingRect = RectF()
+    protected val targetClosingRect = RectF()
+    protected val currentClosingRect = RectF()
 
-    private val startEnteringRect = RectF()
-    private val targetEnteringRect = RectF()
-    private val currentEnteringRect = RectF()
+    protected val startEnteringRect = RectF()
+    protected val targetEnteringRect = RectF()
+    protected val currentEnteringRect = RectF()
 
-    private val backAnimRect = Rect()
+    protected val backAnimRect = Rect()
     private val cropRect = Rect()
 
     private var cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
 
-    private val backAnimationRunner = BackAnimationRunner(
-        Callback(), Runner(), context, Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY
-    )
+    private val backAnimationRunner =
+        BackAnimationRunner(Callback(), Runner(), context, Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY)
     private val initialTouchPos = PointF()
     private val transformMatrix = Matrix()
     private val tmpFloat9 = FloatArray(9)
-    private var enteringTarget: RemoteAnimationTarget? = null
-    private var closingTarget: RemoteAnimationTarget? = null
-    private val transaction = SurfaceControl.Transaction()
+    protected var enteringTarget: RemoteAnimationTarget? = null
+    protected var closingTarget: RemoteAnimationTarget? = null
     private var triggerBack = false
     private var finishCallback: IRemoteAnimationFinishedCallback? = null
     private val progressAnimator = BackProgressAnimator()
     private val displayBoundsMargin =
         context.resources.getDimension(R.dimen.cross_task_back_vertical_margin)
-    private val enteringStartOffset =
-        context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset)
 
     private val gestureInterpolator = Interpolators.BACK_GESTURE
-    private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN
     private val verticalMoveInterpolator: Interpolator = DecelerateInterpolator()
 
     private var scrimLayer: SurfaceControl? = null
@@ -103,13 +98,42 @@
     private var rightLetterboxLayer: SurfaceControl? = null
     private var letterboxColor: Int = 0
 
+    /** Background color to be used during the animation, also see [getBackgroundColor] */
+    protected var customizedBackgroundColor = 0
+
+    /**
+     * Whether the entering target should be shifted vertically with the user gesture in pre-commit
+     */
+    abstract val allowEnteringYShift: Boolean
+
+    /**
+     * Subclasses must set the [startEnteringRect] and [targetEnteringRect] to define the movement
+     * of the enteringTarget during pre-commit phase.
+     */
+    abstract fun preparePreCommitEnteringRectMovement()
+
+    /**
+     * Returns a base transformation to apply to the entering target during pre-commit. The system
+     * will apply the default animation on top of it.
+     */
+    protected open fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation? =
+        null
+
     override fun onConfigurationChanged(newConfiguration: Configuration) {
         cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
     }
 
     override fun getRunner() = backAnimationRunner
 
-    private fun startBackAnimation(backMotionEvent: BackMotionEvent) {
+    private fun getBackgroundColor(): Int =
+        when {
+            customizedBackgroundColor != 0 -> customizedBackgroundColor
+            isLetterboxed -> letterboxColor
+            enteringTarget != null -> enteringTarget!!.taskInfo.taskDescription!!.backgroundColor
+            else -> 0
+        }
+
+    protected open fun startBackAnimation(backMotionEvent: BackMotionEvent) {
         if (enteringTarget == null || closingTarget == null) {
             ProtoLog.d(
                 ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW,
@@ -122,8 +146,8 @@
 
         transaction.setAnimationTransaction()
         isLetterboxed = closingTarget!!.taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed
-        enteringHasSameLetterbox = isLetterboxed &&
-                closingTarget!!.localBounds.equals(enteringTarget!!.localBounds)
+        enteringHasSameLetterbox =
+            isLetterboxed && closingTarget!!.localBounds.equals(enteringTarget!!.localBounds)
 
         if (isLetterboxed && !enteringHasSameLetterbox) {
             // Play animation with letterboxes, if closing and entering target have mismatching
@@ -143,32 +167,27 @@
         targetClosingRect.scaleCentered(MAX_SCALE)
         if (backMotionEvent.swipeEdge != BackEvent.EDGE_RIGHT) {
             targetClosingRect.offset(
-                startClosingRect.right - targetClosingRect.right - displayBoundsMargin, 0f
+                startClosingRect.right - targetClosingRect.right - displayBoundsMargin,
+                0f
             )
         }
 
-        // the entering target starts 96dp to the left of the screen edge...
-        startEnteringRect.set(startClosingRect)
-        startEnteringRect.offset(-enteringStartOffset, 0f)
+        preparePreCommitEnteringRectMovement()
 
-        // ...and gets scaled in sync with the closing target
-        targetEnteringRect.set(startEnteringRect)
-        targetEnteringRect.scaleCentered(MAX_SCALE)
-
-        // Draw background with task background color (or letterbox color).
-        val backgroundColor = if (isLetterboxed) {
-            letterboxColor
-        } else {
-            enteringTarget!!.taskInfo.taskDescription!!.backgroundColor
-        }
         background.ensureBackground(
-            closingTarget!!.windowConfiguration.bounds, backgroundColor, transaction
+            closingTarget!!.windowConfiguration.bounds,
+            getBackgroundColor(),
+            transaction
         )
         ensureScrimLayer()
         if (isLetterboxed && enteringHasSameLetterbox) {
             // crop left and right letterboxes
-            cropRect.set(closingTarget!!.localBounds.left, 0, closingTarget!!.localBounds.right,
-                    closingTarget!!.windowConfiguration.bounds.height())
+            cropRect.set(
+                closingTarget!!.localBounds.left,
+                0,
+                closingTarget!!.localBounds.right,
+                closingTarget!!.windowConfiguration.bounds.height()
+            )
             // and add fake letterbox square surfaces instead
             ensureLetterboxes()
         } else {
@@ -185,8 +204,14 @@
         currentClosingRect.offset(0f, yOffset)
         applyTransform(closingTarget?.leash, currentClosingRect, 1f)
         currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
-        currentEnteringRect.offset(0f, yOffset)
-        applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
+        if (allowEnteringYShift) currentEnteringRect.offset(0f, yOffset)
+        val enteringTransformation = getPreCommitEnteringBaseTransformation(progress)
+        applyTransform(
+            enteringTarget?.leash,
+            currentEnteringRect,
+            enteringTransformation?.alpha ?: 1f,
+            enteringTransformation
+        )
         applyTransaction()
     }
 
@@ -199,30 +224,25 @@
         val deltaYRatio = min(screenHeight / 2f, abs(rawYDelta)) / (screenHeight / 2f)
         val interpolatedYRatio: Float = verticalMoveInterpolator.getInterpolation(deltaYRatio)
         // limit y-shift so surface never passes 8dp screen margin
-        val deltaY = yDirection * interpolatedYRatio * max(
-            0f, (screenHeight - centeredRect.height()) / 2f - displayBoundsMargin
-        )
+        val deltaY =
+            max(0f, (screenHeight - centeredRect.height()) / 2f - displayBoundsMargin) *
+                interpolatedYRatio *
+                yDirection
         return deltaY
     }
 
-    private fun onGestureCommitted() {
-        if (closingTarget?.leash == null || enteringTarget?.leash == null ||
-                !enteringTarget!!.leash.isValid || !closingTarget!!.leash.isValid
+    protected open fun onGestureCommitted() {
+        if (
+            closingTarget?.leash == null ||
+                enteringTarget?.leash == null ||
+                !enteringTarget!!.leash.isValid ||
+                !closingTarget!!.leash.isValid
         ) {
             finishAnimation()
             return
         }
 
-        // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current
-        // coordinate of the gesture driven phase. Let's update the start and target rects and kick
-        // off the animator
-        startClosingRect.set(currentClosingRect)
-        startEnteringRect.set(currentEnteringRect)
-        targetEnteringRect.set(backAnimRect)
-        targetClosingRect.set(backAnimRect)
-        targetClosingRect.offset(currentClosingRect.left + enteringStartOffset, 0f)
-
-        val valueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(POST_ANIMATION_DURATION)
+        val valueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(POST_COMMIT_DURATION)
         valueAnimator.addUpdateListener { animation: ValueAnimator ->
             val progress = animation.animatedFraction
             onPostCommitProgress(progress)
@@ -230,27 +250,22 @@
                 background.resetStatusBarCustomization()
             }
         }
-        valueAnimator.addListener(object : AnimatorListenerAdapter() {
-            override fun onAnimationEnd(animation: Animator) {
-                background.resetStatusBarCustomization()
-                finishAnimation()
+        valueAnimator.addListener(
+            object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator) {
+                    background.resetStatusBarCustomization()
+                    finishAnimation()
+                }
             }
-        })
+        )
         valueAnimator.start()
     }
 
-    private fun onPostCommitProgress(linearProgress: Float) {
-        val closingAlpha = max(1f - linearProgress * 2, 0f)
-        val progress = postCommitInterpolator.getInterpolation(linearProgress)
+    protected open fun onPostCommitProgress(linearProgress: Float) {
         scrimLayer?.let { transaction.setAlpha(it, maxScrimAlpha * (1f - linearProgress)) }
-        currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
-        applyTransform(closingTarget?.leash, currentClosingRect, closingAlpha)
-        currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
-        applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
-        applyTransaction()
     }
 
-    private fun finishAnimation() {
+    protected open fun finishAnimation() {
         enteringTarget?.let {
             if (it.leash != null && it.leash.isValid) {
                 transaction.setCornerRadius(it.leash, 0f)
@@ -278,47 +293,56 @@
         enteringHasSameLetterbox = false
     }
 
-    private fun applyTransform(leash: SurfaceControl?, rect: RectF, alpha: Float) {
+    protected fun applyTransform(
+        leash: SurfaceControl?,
+        rect: RectF,
+        alpha: Float,
+        baseTransformation: Transformation? = null
+    ) {
         if (leash == null || !leash.isValid) return
         val scale = rect.width() / backAnimRect.width()
-        transformMatrix.reset()
-        val scalePivotX = if (isLetterboxed && enteringHasSameLetterbox) {
-            closingTarget!!.localBounds.left.toFloat()
-        } else {
-            0f
-        }
-        transformMatrix.setScale(scale, scale, scalePivotX, 0f)
-        transformMatrix.postTranslate(rect.left, rect.top)
-        transaction.setAlpha(leash, alpha)
-            .setMatrix(leash, transformMatrix, tmpFloat9)
+        val matrix = baseTransformation?.matrix ?: transformMatrix.apply { reset() }
+        val scalePivotX =
+            if (isLetterboxed && enteringHasSameLetterbox) {
+                closingTarget!!.localBounds.left.toFloat()
+            } else {
+                0f
+            }
+        matrix.postScale(scale, scale, scalePivotX, 0f)
+        matrix.postTranslate(rect.left, rect.top)
+        transaction
+            .setAlpha(leash, keepMinimumAlpha(alpha))
+            .setMatrix(leash, matrix, tmpFloat9)
             .setCrop(leash, cropRect)
             .setCornerRadius(leash, cornerRadius)
     }
 
-    private fun applyTransaction() {
-        transaction.setFrameTimelineVsync(Choreographer.getInstance().vsyncId)
+    protected fun applyTransaction() {
+        transaction.setFrameTimelineVsync(choreographer.vsyncId)
         transaction.apply()
     }
 
     private fun ensureScrimLayer() {
         if (scrimLayer != null) return
         val isDarkTheme: Boolean = isDarkMode(context)
-        val scrimBuilder = SurfaceControl.Builder()
-            .setName("Cross-Activity back animation scrim")
-            .setCallsite("CrossActivityBackAnimation")
-            .setColorLayer()
-            .setOpaque(false)
-            .setHidden(false)
+        val scrimBuilder =
+            SurfaceControl.Builder()
+                .setName("Cross-Activity back animation scrim")
+                .setCallsite("CrossActivityBackAnimation")
+                .setColorLayer()
+                .setOpaque(false)
+                .setHidden(false)
 
         rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, scrimBuilder)
         scrimLayer = scrimBuilder.build()
         val colorComponents = floatArrayOf(0f, 0f, 0f)
         maxScrimAlpha = if (isDarkTheme) MAX_SCRIM_ALPHA_DARK else MAX_SCRIM_ALPHA_LIGHT
-        val scrimCrop = if (isLetterboxed) {
-            closingTarget!!.windowConfiguration.bounds
-        } else {
-            closingTarget!!.localBounds
-        }
+        val scrimCrop =
+            if (isLetterboxed) {
+                closingTarget!!.windowConfiguration.bounds
+            } else {
+                closingTarget!!.localBounds
+            }
         transaction
             .setColor(scrimLayer, colorComponents)
             .setAlpha(scrimLayer!!, maxScrimAlpha)
@@ -339,21 +363,34 @@
     private fun ensureLetterboxes() {
         closingTarget?.let { t ->
             if (t.localBounds.left != 0 && leftLetterboxLayer == null) {
-                val bounds = Rect(0, t.windowConfiguration.bounds.top, t.localBounds.left,
-                        t.windowConfiguration.bounds.bottom)
+                val bounds =
+                    Rect(
+                        0,
+                        t.windowConfiguration.bounds.top,
+                        t.localBounds.left,
+                        t.windowConfiguration.bounds.bottom
+                    )
                 leftLetterboxLayer = ensureLetterbox(bounds)
             }
-            if (t.localBounds.right != t.windowConfiguration.bounds.right &&
-                    rightLetterboxLayer == null) {
-                val bounds = Rect(t.localBounds.right, t.windowConfiguration.bounds.top,
-                        t.windowConfiguration.bounds.right, t.windowConfiguration.bounds.bottom)
+            if (
+                t.localBounds.right != t.windowConfiguration.bounds.right &&
+                    rightLetterboxLayer == null
+            ) {
+                val bounds =
+                    Rect(
+                        t.localBounds.right,
+                        t.windowConfiguration.bounds.top,
+                        t.windowConfiguration.bounds.right,
+                        t.windowConfiguration.bounds.bottom
+                    )
                 rightLetterboxLayer = ensureLetterbox(bounds)
             }
         }
     }
 
     private fun ensureLetterbox(bounds: Rect): SurfaceControl {
-        val letterboxBuilder = SurfaceControl.Builder()
+        val letterboxBuilder =
+            SurfaceControl.Builder()
                 .setName("Cross-Activity back animation letterbox")
                 .setCallsite("CrossActivityBackAnimation")
                 .setColorLayer()
@@ -362,13 +399,17 @@
 
         rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, letterboxBuilder)
         val layer = letterboxBuilder.build()
-        val colorComponents = floatArrayOf(Color.red(letterboxColor) / 255f,
-                Color.green(letterboxColor) / 255f, Color.blue(letterboxColor) / 255f)
+        val colorComponents =
+            floatArrayOf(
+                Color.red(letterboxColor) / 255f,
+                Color.green(letterboxColor) / 255f,
+                Color.blue(letterboxColor) / 255f
+            )
         transaction
-                .setColor(layer, colorComponents)
-                .setCrop(layer, bounds)
-                .setRelativeLayer(layer, closingTarget!!.leash, 1)
-                .show(layer)
+            .setColor(layer, colorComponents)
+            .setCrop(layer, bounds)
+            .setRelativeLayer(layer, closingTarget!!.leash, 1)
+            .show(layer)
         return layer
     }
 
@@ -389,8 +430,8 @@
     }
 
     override fun prepareNextAnimation(
-            animationInfo: BackNavigationInfo.CustomAnimationInfo?,
-            letterboxColor: Int
+        animationInfo: BackNavigationInfo.CustomAnimationInfo?,
+        letterboxColor: Int
     ): Boolean {
         this.letterboxColor = letterboxColor
         return false
@@ -415,9 +456,7 @@
         }
 
         override fun onBackCancelled() {
-            progressAnimator.onBackCancelled {
-                finishAnimation()
-            }
+            progressAnimator.onBackCancelled { finishAnimation() }
         }
 
         override fun onBackInvoked() {
@@ -435,7 +474,8 @@
             finishedCallback: IRemoteAnimationFinishedCallback
         ) {
             ProtoLog.d(
-                ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "Start back to activity animation."
+                ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW,
+                "Start back to activity animation."
             )
             for (a in apps) {
                 when (a.mode) {
@@ -452,23 +492,25 @@
     }
 
     companion object {
-        /** Max scale of the entering/closing window.*/
-        private const val MAX_SCALE = 0.9f
-
-        /** Duration of post animation after gesture committed.  */
-        private const val POST_ANIMATION_DURATION = 300L
-
+        /** Max scale of the closing window. */
+        internal const val MAX_SCALE = 0.9f
         private const val MAX_SCRIM_ALPHA_DARK = 0.8f
         private const val MAX_SCRIM_ALPHA_LIGHT = 0.2f
+        private const val POST_COMMIT_DURATION = 300L
     }
 }
 
+// The target will loose focus when alpha == 0, so keep a minimum value for it.
+private fun keepMinimumAlpha(transAlpha: Float): Float {
+    return max(transAlpha.toDouble(), 0.005).toFloat()
+}
+
 private fun isDarkMode(context: Context): Boolean {
     return context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
-            Configuration.UI_MODE_NIGHT_YES
+        Configuration.UI_MODE_NIGHT_YES
 }
 
-private fun RectF.setInterpolatedRectF(start: RectF, target: RectF, progress: Float) {
+internal fun RectF.setInterpolatedRectF(start: RectF, target: RectF, progress: Float) {
     require(!(progress < 0 || progress > 1)) { "Progress value must be between 0 and 1" }
     left = start.left + (target.left - start.left) * progress
     top = start.top + (target.top - start.top) * progress
@@ -476,7 +518,7 @@
     bottom = start.bottom + (target.bottom - start.bottom) * progress
 }
 
-private fun RectF.scaleCentered(
+internal fun RectF.scaleCentered(
     scale: Float,
     pivotX: Float = left + width() / 2,
     pivotY: Float = top + height() / 2
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
new file mode 100644
index 0000000..e6ec2b4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.back
+
+import android.content.Context
+import android.graphics.Rect
+import android.graphics.RectF
+import android.util.MathUtils
+import android.view.Choreographer
+import android.view.SurfaceControl
+import android.view.animation.Animation
+import android.view.animation.Transformation
+import android.window.BackMotionEvent
+import android.window.BackNavigationInfo
+import com.android.internal.R
+import com.android.internal.policy.TransitionAnimation
+import com.android.internal.protolog.common.ProtoLog
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import javax.inject.Inject
+
+/** Class that handles customized predictive cross activity back animations. */
+@ShellMainThread
+class CustomCrossActivityBackAnimation(
+    context: Context,
+    background: BackAnimationBackground,
+    rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+    transaction: SurfaceControl.Transaction,
+    choreographer: Choreographer,
+    private val customAnimationLoader: CustomAnimationLoader
+) :
+    CrossActivityBackAnimation(
+        context,
+        background,
+        rootTaskDisplayAreaOrganizer,
+        transaction,
+        choreographer
+    ) {
+
+    private var enterAnimation: Animation? = null
+    private var closeAnimation: Animation? = null
+    private val transformation = Transformation()
+    private var gestureProgress = 0f
+
+    override val allowEnteringYShift = false
+
+    @Inject
+    constructor(
+        context: Context,
+        background: BackAnimationBackground,
+        rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+    ) : this(
+        context,
+        background,
+        rootTaskDisplayAreaOrganizer,
+        SurfaceControl.Transaction(),
+        Choreographer.getInstance(),
+        CustomAnimationLoader(
+            TransitionAnimation(context, false /* debug */, "CustomCrossActivityBackAnimation")
+        )
+    )
+
+    override fun preparePreCommitEnteringRectMovement() {
+        // No movement for the entering rect
+        startEnteringRect.set(startClosingRect)
+        targetEnteringRect.set(startClosingRect)
+    }
+
+    override fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation {
+        gestureProgress = progress
+        transformation.clear()
+        enterAnimation!!.getTransformationAt(progress * PRE_COMMIT_MAX_PROGRESS, transformation)
+        return transformation
+    }
+
+    override fun startBackAnimation(backMotionEvent: BackMotionEvent) {
+        super.startBackAnimation(backMotionEvent)
+        if (
+            closeAnimation == null ||
+                enterAnimation == null ||
+                closingTarget == null ||
+                enteringTarget == null
+        ) {
+            ProtoLog.d(
+                ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW,
+                "Enter animation or close animation is null."
+            )
+            return
+        }
+        initializeAnimation(closeAnimation!!, closingTarget!!.localBounds)
+        initializeAnimation(enterAnimation!!, enteringTarget!!.localBounds)
+    }
+
+    override fun onPostCommitProgress(linearProgress: Float) {
+        super.onPostCommitProgress(linearProgress)
+        if (closingTarget == null || enteringTarget == null) return
+
+        // TODO: Should we use the duration from the custom xml spec for the post-commit animation?
+        applyTransform(closingTarget!!.leash, currentClosingRect, linearProgress, closeAnimation!!)
+        val enteringProgress =
+            MathUtils.lerp(gestureProgress * PRE_COMMIT_MAX_PROGRESS, 1f, linearProgress)
+        applyTransform(
+            enteringTarget!!.leash,
+            currentEnteringRect,
+            enteringProgress,
+            enterAnimation!!
+        )
+        applyTransaction()
+    }
+
+    private fun applyTransform(
+        leash: SurfaceControl,
+        rect: RectF,
+        progress: Float,
+        animation: Animation
+    ) {
+        transformation.clear()
+        animation.getTransformationAt(progress, transformation)
+        applyTransform(leash, rect, transformation.alpha, transformation)
+    }
+
+    override fun finishAnimation() {
+        closeAnimation?.reset()
+        closeAnimation = null
+        enterAnimation?.reset()
+        enterAnimation = null
+        transformation.clear()
+        gestureProgress = 0f
+        super.finishAnimation()
+    }
+
+    /** Load customize animation before animation start. */
+    override fun prepareNextAnimation(
+        animationInfo: BackNavigationInfo.CustomAnimationInfo?,
+        letterboxColor: Int
+    ): Boolean {
+        super.prepareNextAnimation(animationInfo, letterboxColor)
+        if (animationInfo == null) return false
+        customAnimationLoader.loadAll(animationInfo)?.let { result ->
+            closeAnimation = result.closeAnimation
+            enterAnimation = result.enterAnimation
+            customizedBackgroundColor = result.backgroundColor
+            return true
+        }
+        return false
+    }
+
+    class AnimationLoadResult {
+        var closeAnimation: Animation? = null
+        var enterAnimation: Animation? = null
+        var backgroundColor = 0
+    }
+
+    companion object {
+        private const val PRE_COMMIT_MAX_PROGRESS = 0.2f
+    }
+}
+
+/** Helper class to load custom animation. */
+class CustomAnimationLoader(private val transitionAnimation: TransitionAnimation) {
+
+    /**
+     * Load both enter and exit animation for the close activity transition. Note that the result is
+     * only valid if the exit animation has set and loaded success. If the entering animation has
+     * not set(i.e. 0), here will load the default entering animation for it.
+     *
+     * @param animationInfo The information of customize animation, which can be set from
+     *   [Activity.overrideActivityTransition] and/or [LayoutParams.windowAnimations]
+     */
+    fun loadAll(
+        animationInfo: BackNavigationInfo.CustomAnimationInfo
+    ): CustomCrossActivityBackAnimation.AnimationLoadResult? {
+        if (animationInfo.packageName.isEmpty()) return null
+        val close = loadAnimation(animationInfo, false) ?: return null
+        val open = loadAnimation(animationInfo, true)
+        val result = CustomCrossActivityBackAnimation.AnimationLoadResult()
+        result.closeAnimation = close
+        result.enterAnimation = open
+        result.backgroundColor = animationInfo.customBackground
+        return result
+    }
+
+    /**
+     * Load enter or exit animation from CustomAnimationInfo
+     *
+     * @param animationInfo The information for customize animation.
+     * @param enterAnimation true when load for enter animation, false for exit animation.
+     * @return Loaded animation.
+     */
+    fun loadAnimation(
+        animationInfo: BackNavigationInfo.CustomAnimationInfo,
+        enterAnimation: Boolean
+    ): Animation? {
+        var a: Animation? = null
+        // Activity#overrideActivityTransition has higher priority than windowAnimations
+        // Try to get animation from Activity#overrideActivityTransition
+        if (
+            enterAnimation && animationInfo.customEnterAnim != 0 ||
+                !enterAnimation && animationInfo.customExitAnim != 0
+        ) {
+            a =
+                transitionAnimation.loadAppTransitionAnimation(
+                    animationInfo.packageName,
+                    if (enterAnimation) animationInfo.customEnterAnim
+                    else animationInfo.customExitAnim
+                )
+        } else if (animationInfo.windowAnimations != 0) {
+            // try to get animation from LayoutParams#windowAnimations
+            a =
+                transitionAnimation.loadAnimationAttr(
+                    animationInfo.packageName,
+                    animationInfo.windowAnimations,
+                    if (enterAnimation) R.styleable.WindowAnimation_activityCloseEnterAnimation
+                    else R.styleable.WindowAnimation_activityCloseExitAnimation,
+                    false /* translucent */
+                )
+        }
+        // Only allow to load default animation for opening target.
+        if (a == null && enterAnimation) {
+            a = loadDefaultOpenAnimation()
+        }
+        if (a != null) {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a)
+        } else {
+            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "No custom animation loaded")
+        }
+        return a
+    }
+
+    private fun loadDefaultOpenAnimation(): Animation? {
+        return transitionAnimation.loadDefaultAnimationAttr(
+            R.styleable.WindowAnimation_activityCloseEnterAnimation,
+            false /* translucent */
+        )
+    }
+}
+
+private fun initializeAnimation(animation: Animation, bounds: Rect) {
+    val width = bounds.width()
+    val height = bounds.height()
+    animation.initialize(width, height, width, height)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
deleted file mode 100644
index e27b40e..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.back;
-
-import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-import static android.view.RemoteAnimationTarget.MODE_OPENING;
-
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.util.FloatProperty;
-import android.view.Choreographer;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.WindowManager.LayoutParams;
-import android.view.animation.Animation;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Transformation;
-import android.window.BackEvent;
-import android.window.BackMotionEvent;
-import android.window.BackNavigationInfo;
-import android.window.BackProgressAnimator;
-import android.window.IOnBackInvokedCallback;
-
-import com.android.internal.R;
-import com.android.internal.dynamicanimation.animation.SpringAnimation;
-import com.android.internal.dynamicanimation.animation.SpringForce;
-import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.internal.policy.TransitionAnimation;
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.shared.annotations.ShellMainThread;
-
-import javax.inject.Inject;
-
-/** Class that handle customized close activity transition animation. */
-@ShellMainThread
-public class CustomizeActivityAnimation extends ShellBackAnimation {
-    private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
-    private final BackAnimationRunner mBackAnimationRunner;
-    private float mCornerRadius;
-    private final SurfaceControl.Transaction mTransaction;
-    private final BackAnimationBackground mBackground;
-    private RemoteAnimationTarget mEnteringTarget;
-    private RemoteAnimationTarget mClosingTarget;
-    private IRemoteAnimationFinishedCallback mFinishCallback;
-    /** Duration of post animation after gesture committed. */
-    private static final int POST_ANIMATION_DURATION = 250;
-
-    private static final int SCALE_FACTOR = 1000;
-    private final SpringAnimation mProgressSpring;
-    private float mLatestProgress = 0.0f;
-
-    private static final float TARGET_COMMIT_PROGRESS = 0.5f;
-
-    private final float[] mTmpFloat9 = new float[9];
-    private final DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
-
-    final CustomAnimationLoader mCustomAnimationLoader;
-    private Animation mEnterAnimation;
-    private Animation mCloseAnimation;
-    private int mNextBackgroundColor;
-    final Transformation mTransformation = new Transformation();
-
-    private final Choreographer mChoreographer;
-    private final Context mContext;
-
-    @Inject
-    public CustomizeActivityAnimation(Context context, BackAnimationBackground background) {
-        this(context, background, new SurfaceControl.Transaction(), null);
-    }
-
-    CustomizeActivityAnimation(Context context, BackAnimationBackground background,
-            SurfaceControl.Transaction transaction, Choreographer choreographer) {
-        mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
-        mBackground = background;
-        mBackAnimationRunner = new BackAnimationRunner(
-                new Callback(), new Runner(), context, CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY);
-        mCustomAnimationLoader = new CustomAnimationLoader(context);
-
-        mProgressSpring = new SpringAnimation(this, ENTER_PROGRESS_PROP);
-        mProgressSpring.setSpring(new SpringForce()
-                .setStiffness(SpringForce.STIFFNESS_MEDIUM)
-                .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY));
-        mTransaction = transaction == null ? new SurfaceControl.Transaction() : transaction;
-        mChoreographer = choreographer != null ? choreographer : Choreographer.getInstance();
-        mContext = context;
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
-    }
-
-    private float getLatestProgress() {
-        return mLatestProgress * SCALE_FACTOR;
-    }
-    private void setLatestProgress(float value) {
-        mLatestProgress = value / SCALE_FACTOR;
-        applyTransformTransaction(mLatestProgress);
-    }
-
-    private static final FloatProperty<CustomizeActivityAnimation> ENTER_PROGRESS_PROP =
-            new FloatProperty<>("enter") {
-                @Override
-                public void setValue(CustomizeActivityAnimation anim, float value) {
-                    anim.setLatestProgress(value);
-                }
-
-                @Override
-                public Float get(CustomizeActivityAnimation object) {
-                    return object.getLatestProgress();
-                }
-            };
-
-    // The target will lose focus when alpha == 0, so keep a minimum value for it.
-    private static float keepMinimumAlpha(float transAlpha) {
-        return Math.max(transAlpha, 0.005f);
-    }
-
-    private static void initializeAnimation(Animation animation, Rect bounds) {
-        final int width = bounds.width();
-        final int height = bounds.height();
-        animation.initialize(width, height, width, height);
-    }
-
-    private void startBackAnimation() {
-        if (mEnteringTarget == null || mClosingTarget == null
-                || mCloseAnimation == null || mEnterAnimation == null) {
-            ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Entering target or closing target is null.");
-            return;
-        }
-        initializeAnimation(mCloseAnimation, mClosingTarget.localBounds);
-        initializeAnimation(mEnterAnimation, mEnteringTarget.localBounds);
-
-        // Draw background with task background color.
-        if (mEnteringTarget.taskInfo != null && mEnteringTarget.taskInfo.taskDescription != null) {
-            mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(),
-                    mNextBackgroundColor == Color.TRANSPARENT
-                            ? mEnteringTarget.taskInfo.taskDescription.getBackgroundColor()
-                            : mNextBackgroundColor,
-                    mTransaction);
-        }
-    }
-
-    private void applyTransformTransaction(float progress) {
-        if (mClosingTarget == null || mEnteringTarget == null) {
-            return;
-        }
-        applyTransform(mClosingTarget.leash, progress, mCloseAnimation);
-        applyTransform(mEnteringTarget.leash, progress, mEnterAnimation);
-        mTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId());
-        mTransaction.apply();
-    }
-
-    private void applyTransform(SurfaceControl leash, float progress, Animation animation) {
-        mTransformation.clear();
-        animation.getTransformationAt(progress, mTransformation);
-        mTransaction.setMatrix(leash, mTransformation.getMatrix(), mTmpFloat9);
-        mTransaction.setAlpha(leash, keepMinimumAlpha(mTransformation.getAlpha()));
-        mTransaction.setCornerRadius(leash, mCornerRadius);
-    }
-
-    void finishAnimation() {
-        if (mCloseAnimation != null) {
-            mCloseAnimation.reset();
-            mCloseAnimation = null;
-        }
-        if (mEnterAnimation != null) {
-            mEnterAnimation.reset();
-            mEnterAnimation = null;
-        }
-        if (mEnteringTarget != null) {
-            mEnteringTarget.leash.release();
-            mEnteringTarget = null;
-        }
-        if (mClosingTarget != null) {
-            mClosingTarget.leash.release();
-            mClosingTarget = null;
-        }
-        if (mBackground != null) {
-            mBackground.removeBackground(mTransaction);
-        }
-        mTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId());
-        mTransaction.apply();
-        mTransformation.clear();
-        mLatestProgress = 0;
-        mNextBackgroundColor = Color.TRANSPARENT;
-        if (mFinishCallback != null) {
-            try {
-                mFinishCallback.onAnimationFinished();
-            } catch (RemoteException e) {
-                e.printStackTrace();
-            }
-            mFinishCallback = null;
-        }
-        mProgressSpring.animateToFinalPosition(0);
-        mProgressSpring.skipToEnd();
-    }
-
-    void onGestureProgress(@NonNull BackEvent backEvent) {
-        if (mEnteringTarget == null || mClosingTarget == null
-                || mCloseAnimation == null || mEnterAnimation == null) {
-            return;
-        }
-
-        final float progress = backEvent.getProgress();
-
-        float springProgress = (progress > 0.1f
-                ? mapLinear(progress, 0.1f, 1f, TARGET_COMMIT_PROGRESS, 1f)
-                : mapLinear(progress, 0, 1f, 0f, TARGET_COMMIT_PROGRESS)) * SCALE_FACTOR;
-
-        mProgressSpring.animateToFinalPosition(springProgress);
-    }
-
-    static float mapLinear(float x, float a1, float a2, float b1, float b2) {
-        return b1 + (x - a1) * (b2 - b1) / (a2 - a1);
-    }
-
-    void onGestureCommitted() {
-        if (mEnteringTarget == null || mClosingTarget == null
-                || mCloseAnimation == null || mEnterAnimation == null) {
-            finishAnimation();
-            return;
-        }
-        mProgressSpring.cancel();
-
-        // Enter phase 2 of the animation
-        final ValueAnimator valueAnimator = ValueAnimator.ofFloat(mLatestProgress, 1f)
-                .setDuration(POST_ANIMATION_DURATION);
-        valueAnimator.setInterpolator(mDecelerateInterpolator);
-        valueAnimator.addUpdateListener(animation -> {
-            float progress = (float) animation.getAnimatedValue();
-            applyTransformTransaction(progress);
-        });
-
-        valueAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                finishAnimation();
-            }
-        });
-        valueAnimator.start();
-    }
-
-    /** Load customize animation before animation start. */
-    @Override
-    public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo,
-            int letterboxColor) {
-        if (animationInfo == null) {
-            return false;
-        }
-        final AnimationLoadResult result = mCustomAnimationLoader.loadAll(animationInfo);
-        if (result != null) {
-            mCloseAnimation = result.mCloseAnimation;
-            mEnterAnimation = result.mEnterAnimation;
-            mNextBackgroundColor = result.mBackgroundColor;
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public BackAnimationRunner getRunner() {
-        return mBackAnimationRunner;
-    }
-
-    private final class Callback extends IOnBackInvokedCallback.Default {
-        @Override
-        public void onBackStarted(BackMotionEvent backEvent) {
-            // in case we're still animating an onBackCancelled event, let's remove the finish-
-            // callback from the progress animator to prevent calling finishAnimation() before
-            // restarting a new animation
-            mProgressAnimator.removeOnBackCancelledFinishCallback();
-
-            mProgressAnimator.onBackStarted(backEvent,
-                    CustomizeActivityAnimation.this::onGestureProgress);
-        }
-
-        @Override
-        public void onBackProgressed(@NonNull BackMotionEvent backEvent) {
-            mProgressAnimator.onBackProgressed(backEvent);
-        }
-
-        @Override
-        public void onBackCancelled() {
-            mProgressAnimator.onBackCancelled(CustomizeActivityAnimation.this::finishAnimation);
-        }
-
-        @Override
-        public void onBackInvoked() {
-            mProgressAnimator.reset();
-            onGestureCommitted();
-        }
-    }
-
-    private final class Runner extends IRemoteAnimationRunner.Default {
-        @Override
-        public void onAnimationStart(
-                int transit,
-                RemoteAnimationTarget[] apps,
-                RemoteAnimationTarget[] wallpapers,
-                RemoteAnimationTarget[] nonApps,
-                IRemoteAnimationFinishedCallback finishedCallback) {
-            ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Start back to customize animation.");
-            for (RemoteAnimationTarget a : apps) {
-                if (a.mode == MODE_CLOSING) {
-                    mClosingTarget = a;
-                }
-                if (a.mode == MODE_OPENING) {
-                    mEnteringTarget = a;
-                }
-            }
-            if (mCloseAnimation == null || mEnterAnimation == null) {
-                ProtoLog.d(WM_SHELL_BACK_PREVIEW,
-                        "No animation loaded, should choose cross-activity animation?");
-            }
-
-            startBackAnimation();
-            mFinishCallback = finishedCallback;
-        }
-
-        @Override
-        public void onAnimationCancelled() {
-            finishAnimation();
-        }
-    }
-
-
-    static final class AnimationLoadResult {
-        Animation mCloseAnimation;
-        Animation mEnterAnimation;
-        int mBackgroundColor;
-    }
-
-    /**
-     * Helper class to load custom animation.
-     */
-    static class CustomAnimationLoader {
-        final TransitionAnimation mTransitionAnimation;
-
-        CustomAnimationLoader(Context context) {
-            mTransitionAnimation = new TransitionAnimation(
-                    context, false /* debug */, "CustomizeBackAnimation");
-        }
-
-        /**
-         * Load both enter and exit animation for the close activity transition.
-         * Note that the result is only valid if the exit animation has set and loaded success.
-         * If the entering animation has not set(i.e. 0), here will load the default entering
-         * animation for it.
-         *
-         * @param animationInfo The information of customize animation, which can be set from
-         * {@link Activity#overrideActivityTransition} and/or
-         * {@link LayoutParams#windowAnimations}
-         */
-        AnimationLoadResult loadAll(BackNavigationInfo.CustomAnimationInfo animationInfo) {
-            if (animationInfo.getPackageName().isEmpty()) {
-                return null;
-            }
-            final Animation close = loadAnimation(animationInfo, false);
-            if (close == null) {
-                return null;
-            }
-            final Animation open = loadAnimation(animationInfo, true);
-            AnimationLoadResult result = new AnimationLoadResult();
-            result.mCloseAnimation = close;
-            result.mEnterAnimation = open;
-            result.mBackgroundColor = animationInfo.getCustomBackground();
-            return result;
-        }
-
-        /**
-         * Load enter or exit animation from CustomAnimationInfo
-         * @param animationInfo The information for customize animation.
-         * @param enterAnimation true when load for enter animation, false for exit animation.
-         * @return Loaded animation.
-         */
-        @Nullable
-        Animation loadAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo,
-                boolean enterAnimation) {
-            Animation a = null;
-            // Activity#overrideActivityTransition has higher priority than windowAnimations
-            // Try to get animation from Activity#overrideActivityTransition
-            if ((enterAnimation && animationInfo.getCustomEnterAnim() != 0)
-                    || (!enterAnimation && animationInfo.getCustomExitAnim() != 0)) {
-                a = mTransitionAnimation.loadAppTransitionAnimation(
-                        animationInfo.getPackageName(),
-                        enterAnimation ? animationInfo.getCustomEnterAnim()
-                                : animationInfo.getCustomExitAnim());
-            } else if (animationInfo.getWindowAnimations() != 0) {
-                // try to get animation from LayoutParams#windowAnimations
-                a = mTransitionAnimation.loadAnimationAttr(animationInfo.getPackageName(),
-                        animationInfo.getWindowAnimations(), enterAnimation
-                                ? R.styleable.WindowAnimation_activityCloseEnterAnimation
-                                : R.styleable.WindowAnimation_activityCloseExitAnimation,
-                        false /* translucent */);
-            }
-            // Only allow to load default animation for opening target.
-            if (a == null && enterAnimation) {
-                a = loadDefaultOpenAnimation();
-            }
-            if (a != null) {
-                ProtoLog.d(WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a);
-            } else {
-                ProtoLog.e(WM_SHELL_BACK_PREVIEW, "No custom animation loaded");
-            }
-            return a;
-        }
-
-        private Animation loadDefaultOpenAnimation() {
-            return mTransitionAnimation.loadDefaultAnimationAttr(
-                    R.styleable.WindowAnimation_activityCloseEnterAnimation,
-                    false /* translucent */);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
new file mode 100644
index 0000000..f33c5b9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.back
+
+import android.content.Context
+import android.view.Choreographer
+import android.view.SurfaceControl
+import com.android.wm.shell.R
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import javax.inject.Inject
+import kotlin.math.max
+
+/** Class that defines cross-activity animation. */
+@ShellMainThread
+class DefaultCrossActivityBackAnimation
+@Inject
+constructor(
+    context: Context,
+    background: BackAnimationBackground,
+    rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+) :
+    CrossActivityBackAnimation(
+        context,
+        background,
+        rootTaskDisplayAreaOrganizer,
+        SurfaceControl.Transaction(),
+        Choreographer.getInstance()
+    ) {
+
+    private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN
+    private val enteringStartOffset =
+        context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset)
+    override val allowEnteringYShift = true
+
+    override fun preparePreCommitEnteringRectMovement() {
+        // the entering target starts 96dp to the left of the screen edge...
+        startEnteringRect.set(startClosingRect)
+        startEnteringRect.offset(-enteringStartOffset, 0f)
+        // ...and gets scaled in sync with the closing target
+        targetEnteringRect.set(startEnteringRect)
+        targetEnteringRect.scaleCentered(MAX_SCALE)
+    }
+
+    override fun onGestureCommitted() {
+        // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current
+        // coordinate of the gesture driven phase. Let's update the start and target rects and kick
+        // off the animator in the superclass
+        startClosingRect.set(currentClosingRect)
+        startEnteringRect.set(currentEnteringRect)
+        targetEnteringRect.set(backAnimRect)
+        targetClosingRect.set(backAnimRect)
+        targetClosingRect.offset(currentClosingRect.left + enteringStartOffset, 0f)
+        super.onGestureCommitted()
+    }
+
+    override fun onPostCommitProgress(linearProgress: Float) {
+        super.onPostCommitProgress(linearProgress)
+        val closingAlpha = max(1f - linearProgress * 2, 0f)
+        val progress = postCommitInterpolator.getInterpolation(linearProgress)
+        currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
+        applyTransform(closingTarget?.leash, currentClosingRect, closingAlpha)
+        currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
+        applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
+        applyTransaction()
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 9e6c5fb..38c3443 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -87,6 +87,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
@@ -1170,7 +1171,9 @@
      * @param bubbleKey key of the bubble being dragged
      */
     public void startBubbleDrag(String bubbleKey) {
-        onBubbleDrag(bubbleKey, true /* isBeingDragged */);
+        if (mBubbleData.getSelectedBubble() != null) {
+            mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ false);
+        }
         if (mBubbleStateListener != null) {
             boolean overflow = BubbleOverflow.KEY.equals(bubbleKey);
             Rect rect = new Rect();
@@ -1183,23 +1186,29 @@
     }
 
     /**
-     * A bubble is no longer being dragged in Launcher. As was released in given location.
+     * A bubble is no longer being dragged in Launcher. And was released in given location.
      * Will be called only when bubble bar is expanded.
      *
-     * @param bubbleKey key of the bubble being dragged
      * @param location  location where bubble was released
      */
-    public void stopBubbleDrag(String bubbleKey, BubbleBarLocation location) {
+    public void stopBubbleDrag(BubbleBarLocation location) {
         mBubblePositioner.setBubbleBarLocation(location);
-        onBubbleDrag(bubbleKey, false /* isBeingDragged */);
+        if (mBubbleData.getSelectedBubble() != null) {
+            mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true);
+        }
     }
 
-    private void onBubbleDrag(String bubbleKey, boolean isBeingDragged) {
-        // TODO(b/330585402): collapse stack if any bubble is dragged
-        if (mBubbleData.getSelectedBubble() != null
-                && mBubbleData.getSelectedBubble().getKey().equals(bubbleKey)) {
-            // Should collapse/expand only if equals to selected bubble.
-            mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ !isBeingDragged);
+    /**
+     * A bubble was dragged and is released in dismiss target in Launcher.
+     *
+     * @param bubbleKey key of the bubble being dragged to dismiss target
+     */
+    public void dragBubbleToDismiss(String bubbleKey) {
+        String selectedBubbleKey = mBubbleData.getSelectedBubbleKey();
+        removeBubble(bubbleKey, Bubbles.DISMISS_USER_GESTURE);
+        if (selectedBubbleKey != null && !selectedBubbleKey.equals(bubbleKey)) {
+            // We did not remove the selected bubble. Expand it again
+            mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true);
         }
     }
 
@@ -1858,7 +1867,11 @@
 
         @Override
         public void bubbleOverflowChanged(boolean hasBubbles) {
-            // TODO (b/334175587): tell stack view to hide / show the overflow
+            if (Flags.enableOptionalBubbleOverflow()) {
+                if (mStackView != null) {
+                    mStackView.showOverflow(hasBubbles);
+                }
+            }
         }
     };
 
@@ -2358,12 +2371,6 @@
         }
 
         @Override
-        public void removeBubble(String key) {
-            mMainExecutor.execute(
-                    () -> mController.removeBubble(key, Bubbles.DISMISS_USER_GESTURE));
-        }
-
-        @Override
         public void removeAllBubbles() {
             mMainExecutor.execute(() -> mController.removeAllBubbles(Bubbles.DISMISS_USER_GESTURE));
         }
@@ -2379,8 +2386,13 @@
         }
 
         @Override
-        public void stopBubbleDrag(String bubbleKey, BubbleBarLocation location) {
-            mMainExecutor.execute(() -> mController.stopBubbleDrag(bubbleKey, location));
+        public void stopBubbleDrag(BubbleBarLocation location) {
+            mMainExecutor.execute(() -> mController.stopBubbleDrag(location));
+        }
+
+        @Override
+        public void dragBubbleToDismiss(String key) {
+            mMainExecutor.execute(() -> mController.dragBubbleToDismiss(key));
         }
 
         @Override
@@ -2397,7 +2409,10 @@
 
         @Override
         public void setBubbleBarBounds(Rect bubbleBarBounds) {
-            mMainExecutor.execute(() -> mBubblePositioner.setBubbleBarBounds(bubbleBarBounds));
+            mMainExecutor.execute(() -> {
+                mBubblePositioner.setBubbleBarBounds(bubbleBarBounds);
+                if (mLayerView != null) mLayerView.updateExpandedView();
+            });
         }
     }
 
@@ -2709,6 +2724,15 @@
                     () -> BubbleController.this.onSensitiveNotificationProtectionStateChanged(
                             sensitiveNotificationProtectionActive));
         }
+
+        @Override
+        public boolean canShowBubbleNotification() {
+            // in bubble bar mode, when the IME is visible we can't animate new bubbles.
+            if (BubbleController.this.isShowingAsBubbleBar()) {
+                return !BubbleController.this.mBubblePositioner.getIsImeVisible();
+            }
+            return true;
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index ea30af5..26483c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -327,6 +327,14 @@
         return mSelectedBubble;
     }
 
+    /**
+     * Returns the key of the selected bubble, or null if no bubble is selected.
+     */
+    @Nullable
+    public String getSelectedBubbleKey() {
+        return mSelectedBubble != null ? mSelectedBubble.getKey() : null;
+    }
+
     public BubbleOverflow getOverflow() {
         return mOverflow;
     }
@@ -1228,9 +1236,7 @@
     public void dump(PrintWriter pw) {
         pw.println("BubbleData state:");
         pw.print("  selected: ");
-        pw.println(mSelectedBubble != null
-                ? mSelectedBubble.getKey()
-                : "null");
+        pw.println(getSelectedBubbleKey());
         pw.print("  expanded: ");
         pw.println(mExpanded);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index 633b01b..18e04d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -44,6 +44,7 @@
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ContrastColorUtil;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
 
 import java.util.ArrayList;
@@ -195,7 +196,9 @@
     }
 
     void updateEmptyStateVisibility() {
-        mEmptyState.setVisibility(mOverflowBubbles.isEmpty() ? View.VISIBLE : View.GONE);
+        boolean showEmptyState = mOverflowBubbles.isEmpty()
+                && !Flags.enableOptionalBubbleOverflow();
+        mEmptyState.setVisibility(showEmptyState ? View.VISIBLE : View.GONE);
         mRecyclerView.setVisibility(mOverflowBubbles.isEmpty() ? View.GONE : View.VISIBLE);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index c4bbe32..a35a004 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -324,6 +324,11 @@
         return 0;
     }
 
+    /** Returns whether the IME is visible. */
+    public boolean getIsImeVisible() {
+        return mImeVisible;
+    }
+
     /** Sets whether the IME is visible. **/
     public void setImeVisible(boolean visible, int height) {
         mImeVisible = visible;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index be88b34..9fabd42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -80,6 +80,7 @@
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener;
@@ -863,6 +864,7 @@
         }
     };
 
+    private boolean mShowingOverflow;
     private BubbleOverflow mBubbleOverflow;
     private StackEducationView mStackEduView;
     private StackEducationView.Manager mStackEducationViewManager;
@@ -992,18 +994,12 @@
 
         mBubbleOverflow = mBubbleData.getOverflow();
 
-        resetOverflowView();
-        mBubbleContainer.addView(mBubbleOverflow.getIconView(),
-                mBubbleContainer.getChildCount() /* index */,
-                new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
-                        mPositioner.getBubbleSize()));
-        updateOverflow();
-        mBubbleOverflow.getIconView().setOnClickListener((View v) -> {
-            mBubbleData.setShowingOverflow(true);
-            mBubbleData.setSelectedBubble(mBubbleOverflow);
-            mBubbleData.setExpanded(true);
-        });
-
+        if (Flags.enableOptionalBubbleOverflow()) {
+            showOverflow(mBubbleData.hasOverflowBubbles());
+        } else {
+            mShowingOverflow = true; // if the flags not on this is always true
+            setUpOverflow();
+        }
         mScrim = new View(getContext());
         mScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
         mScrim.setBackgroundDrawable(new ColorDrawable(
@@ -1220,6 +1216,19 @@
         }
     };
 
+    private void setUpOverflow() {
+        resetOverflowView();
+        mBubbleContainer.addView(mBubbleOverflow.getIconView(),
+                mBubbleContainer.getChildCount() /* index */,
+                new FrameLayout.LayoutParams(mBubbleSize, mBubbleSize));
+        updateOverflow();
+        mBubbleOverflow.getIconView().setOnClickListener((View v) -> {
+            mBubbleData.setShowingOverflow(true);
+            mBubbleData.setSelectedBubble(mBubbleOverflow);
+            mBubbleData.setExpanded(true);
+        });
+    }
+
     private void setUpDismissView() {
         if (mDismissView != null) {
             removeView(mDismissView);
@@ -1458,24 +1467,56 @@
                 b.getExpandedView().updateFontSize();
             }
         }
-        if (mBubbleOverflow != null && mBubbleOverflow.getExpandedView() != null) {
+        if (mShowingOverflow && mBubbleOverflow != null
+                && mBubbleOverflow.getExpandedView() != null) {
             mBubbleOverflow.getExpandedView().updateFontSize();
         }
     }
 
     void updateLocale() {
-        if (mBubbleOverflow != null && mBubbleOverflow.getExpandedView() != null) {
+        if (mShowingOverflow && mBubbleOverflow != null
+                && mBubbleOverflow.getExpandedView() != null) {
             mBubbleOverflow.getExpandedView().updateLocale();
         }
     }
 
     private void updateOverflow() {
         mBubbleOverflow.update();
-        mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
-                mBubbleContainer.getChildCount() - 1 /* index */);
+        if (mShowingOverflow) {
+            mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
+                    mBubbleContainer.getChildCount() - 1 /* index */);
+        }
         updateOverflowVisibility();
     }
 
+    private void updateOverflowVisibility() {
+        mBubbleOverflow.setVisible(mShowingOverflow
+                && (mIsExpanded || mBubbleData.isShowingOverflow())
+                ? VISIBLE
+                : GONE);
+    }
+
+    private void updateOverflowDotVisibility(boolean expanding) {
+        if (mShowingOverflow && mBubbleOverflow.showDot()) {
+            mBubbleOverflow.getIconView().animateDotScale(expanding ? 1 : 0f, () -> {
+                mBubbleOverflow.setVisible(expanding ? VISIBLE : GONE);
+            });
+        }
+    }
+
+    /**  Sets whether the overflow should be visible or not. */
+    public void showOverflow(boolean showOverflow) {
+        if (!Flags.enableOptionalBubbleOverflow()) return;
+        if (mShowingOverflow != showOverflow) {
+            mShowingOverflow = showOverflow;
+            if (showOverflow) {
+                setUpOverflow();
+            } else if (mBubbleOverflow != null) {
+                resetOverflowView();
+            }
+        }
+    }
+
     /**
      * Handle theme changes.
      */
@@ -1535,7 +1576,10 @@
                 b.getExpandedView().updateDimensions();
             }
         }
-        mBubbleOverflow.getIconView().setLayoutParams(new LayoutParams(mBubbleSize, mBubbleSize));
+        if (mShowingOverflow) {
+            mBubbleOverflow.getIconView().setLayoutParams(
+                    new LayoutParams(mBubbleSize, mBubbleSize));
+        }
         mExpandedAnimationController.updateResources();
         mStackAnimationController.updateResources();
         mDismissView.updateResources();
@@ -1699,7 +1743,7 @@
                     bubble.getIconView().setContentDescription(getResources().getString(
                             R.string.bubble_content_description_single, titleStr, appName));
                 } else {
-                    final int moreCount = mBubbleContainer.getChildCount() - 1;
+                    final int moreCount = getBubbleCount();
                     bubble.getIconView().setContentDescription(getResources().getString(
                             R.string.bubble_content_description_stack,
                             titleStr, appName, moreCount));
@@ -1752,7 +1796,8 @@
 
             View bubbleOverflowIconView =
                     mBubbleOverflow != null ? mBubbleOverflow.getIconView() : null;
-            if (bubbleOverflowIconView != null && !mBubbleData.getBubbles().isEmpty()) {
+            if (mShowingOverflow && bubbleOverflowIconView != null
+                    && !mBubbleData.getBubbles().isEmpty()) {
                 Bubble lastBubble =
                         mBubbleData.getBubbles().get(mBubbleData.getBubbles().size() - 1);
                 View lastBubbleIconView = lastBubble.getIconView();
@@ -1928,20 +1973,6 @@
         }
     }
 
-    private void updateOverflowVisibility() {
-        mBubbleOverflow.setVisible((mIsExpanded || mBubbleData.isShowingOverflow())
-                ? VISIBLE
-                : GONE);
-    }
-
-    private void updateOverflowDotVisibility(boolean expanding) {
-        if (mBubbleOverflow.showDot()) {
-            mBubbleOverflow.getIconView().animateDotScale(expanding ? 1 : 0f, () -> {
-                mBubbleOverflow.setVisible(expanding ? VISIBLE : GONE);
-            });
-        }
-    }
-
     // via BubbleData.Listener
     void updateBubble(Bubble bubble) {
         animateInFlyoutForBubble(bubble);
@@ -3428,8 +3459,9 @@
      * @return the number of bubbles in the stack view.
      */
     public int getBubbleCount() {
-        // Subtract 1 for the overflow button that is always in the bubble container.
-        return mBubbleContainer.getChildCount() - 1;
+        final int childCount = mBubbleContainer.getChildCount();
+        // Subtract 1 for the overflow button if it's showing.
+        return mShowingOverflow ? childCount - 1 : childCount;
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 322088b..1d053f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -297,6 +297,15 @@
             boolean sensitiveNotificationProtectionActive);
 
     /**
+     * Determines whether Bubbles can show notifications.
+     *
+     * <p>Normally bubble notifications are shown by Bubbles, but in some cases the bubble
+     * notification is suppressed and should be shown by the Notifications pipeline as regular
+     * notifications.
+     */
+    boolean canShowBubbleNotification();
+
+    /**
      * A listener to be notified of bubble state changes, used by launcher to render bubbles in
      * its process.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 66f77fa..1eff149 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -33,7 +33,7 @@
 
     oneway void showBubble(in String key, in Rect bubbleBarBounds) = 3;
 
-    oneway void removeBubble(in String key) = 4;
+    oneway void dragBubbleToDismiss(in String key) = 4;
 
     oneway void removeAllBubbles() = 5;
 
@@ -47,5 +47,5 @@
 
     oneway void setBubbleBarBounds(in Rect bubbleBarBounds) = 10;
 
-    oneway void stopBubbleDrag(in String key, in BubbleBarLocation location) = 11;
+    oneway void stopBubbleDrag(in BubbleBarLocation location) = 11;
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index a351cef..123cc7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -356,7 +356,7 @@
     }
 
     /** Updates the expanded view size and position. */
-    private void updateExpandedView() {
+    public void updateExpandedView() {
         if (mExpandedView == null || mExpandedBubble == null) return;
         boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
         mPositioner.getBubbleBarExpandedViewBounds(mPositioner.isBubbleBarOnLeft(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index dba0a98..579a794 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -152,7 +152,8 @@
                 "org.chromium.arc", 0)
             val isTv = AppGlobals.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_LEANBACK, 0)
-            isPip2ExperimentEnabled = SystemProperties.getBoolean("wm_shell.pip2", false) ||
+            isPip2ExperimentEnabled = SystemProperties.getBoolean(
+                    "persist.wm_shell.pip2", false) ||
                     (Flags.enablePip2Implementation() && !isArc && !isTv)
         }
         return isPip2ExperimentEnabled as Boolean
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 5c292f1..bfac24b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -188,6 +188,11 @@
      */
     private boolean mHasShownUserAspectRatioSettingsButton = false;
 
+    /**
+     * This is true when the rechability education is displayed for the first time.
+     */
+    private boolean mIsFirstReachabilityEducationRunning;
+
     public CompatUIController(@NonNull Context context,
             @NonNull ShellInit shellInit,
             @NonNull ShellController shellController,
@@ -252,9 +257,35 @@
             removeLayouts(taskInfo.taskId);
             return;
         }
-
+        // We're showing the first reachability education so we ignore incoming TaskInfo
+        // until the education flow has completed or we double tap.
+        if (mIsFirstReachabilityEducationRunning) {
+            return;
+        }
+        if (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed) {
+            if (taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled) {
+                createOrUpdateLetterboxEduLayout(taskInfo, taskListener);
+            } else if (!taskInfo.appCompatTaskInfo.isFromLetterboxDoubleTap) {
+                // In this case the app is letterboxed and the letterbox education
+                // is disabled. In this case we need to understand if it's the first
+                // time we show the reachability education. When this is happening
+                // we need to ignore all the incoming TaskInfo until the education
+                // completes. If we come from a double tap we follow the normal flow.
+                final boolean topActivityPillarboxed =
+                        taskInfo.appCompatTaskInfo.isTopActivityPillarboxed();
+                final boolean isFirstTimeHorizontalReachabilityEdu = topActivityPillarboxed
+                        && !mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(taskInfo);
+                final boolean isFirstTimeVerticalReachabilityEdu = !topActivityPillarboxed
+                        && !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(taskInfo);
+                if (isFirstTimeHorizontalReachabilityEdu || isFirstTimeVerticalReachabilityEdu) {
+                    mIsFirstReachabilityEducationRunning = true;
+                    mCompatUIConfiguration.setSeenLetterboxEducation(taskInfo.userId);
+                    createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+                    return;
+                }
+            }
+        }
         createOrUpdateCompatLayout(taskInfo, taskListener);
-        createOrUpdateLetterboxEduLayout(taskInfo, taskListener);
         createOrUpdateRestartDialogLayout(taskInfo, taskListener);
         if (mCompatUIConfiguration.getHasSeenLetterboxEducation(taskInfo.userId)) {
             createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
@@ -589,6 +620,7 @@
     private void onInitialReachabilityEduDismissed(@NonNull TaskInfo taskInfo,
             @NonNull ShellTaskOrganizer.TaskListener taskListener) {
         // We need to update the UI otherwise it will not be shown until the user relaunches the app
+        mIsFirstReachabilityEducationRunning = false;
         createOrUpdateUserAspectRatioSettingsLayout(taskInfo, taskListener);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index e729c7d..991fbaf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -72,7 +72,6 @@
 import com.android.wm.shell.compatui.CompatUIController;
 import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
 import com.android.wm.shell.desktopmode.DesktopMode;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
@@ -88,6 +87,7 @@
 import com.android.wm.shell.recents.RecentTasks;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.shared.ShellTransitions;
 import com.android.wm.shell.shared.annotations.ShellAnimationThread;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index a1910c5..fb0a1ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -57,7 +57,6 @@
 import com.android.wm.shell.dagger.pip.PipModule;
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
 import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
@@ -77,6 +76,7 @@
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.shared.annotations.ShellAnimationThread;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java
index 795bc1a..d2895b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java
@@ -16,9 +16,9 @@
 
 package com.android.wm.shell.dagger.back;
 
-import com.android.wm.shell.back.CrossActivityBackAnimation;
 import com.android.wm.shell.back.CrossTaskBackAnimation;
-import com.android.wm.shell.back.CustomizeActivityAnimation;
+import com.android.wm.shell.back.CustomCrossActivityBackAnimation;
+import com.android.wm.shell.back.DefaultCrossActivityBackAnimation;
 import com.android.wm.shell.back.ShellBackAnimation;
 import com.android.wm.shell.back.ShellBackAnimationRegistry;
 
@@ -47,7 +47,7 @@
     @Binds
     @ShellBackAnimation.CrossActivity
     ShellBackAnimation bindCrossActivityShellBackAnimation(
-            CrossActivityBackAnimation crossActivityBackAnimation);
+            DefaultCrossActivityBackAnimation defaultCrossActivityBackAnimation);
 
     /** Default cross task back animation */
     @Binds
@@ -59,5 +59,5 @@
     @Binds
     @ShellBackAnimation.CustomizeActivity
     ShellBackAnimation provideCustomizeActivityShellBackAnimation(
-            CustomizeActivityAnimation customizeActivityAnimation);
+            CustomCrossActivityBackAnimation customCrossActivityBackAnimation);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 6e61f22..01364d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -132,6 +132,8 @@
             PhonePipMenuController menuPhoneController,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             @NonNull PipBoundsState pipBoundsState,
+            @NonNull PipTransitionState pipTransitionState,
+            @NonNull PipScheduler pipScheduler,
             @NonNull SizeSpecSource sizeSpecSource,
             PipMotionHelper pipMotionHelper,
             FloatingContentCoordinator floatingContentCoordinator,
@@ -139,8 +141,9 @@
             @ShellMainThread ShellExecutor mainExecutor,
             Optional<PipPerfHintController> pipPerfHintControllerOptional) {
         return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm,
-                pipBoundsState, sizeSpecSource, pipMotionHelper, floatingContentCoordinator,
-                pipUiEventLogger, mainExecutor, pipPerfHintControllerOptional);
+                pipBoundsState, pipTransitionState, pipScheduler, sizeSpecSource, pipMotionHelper,
+                floatingContentCoordinator, pipUiEventLogger, mainExecutor,
+                pipPerfHintControllerOptional);
     }
 
     @WMSingleton
@@ -149,9 +152,13 @@
             PipBoundsState pipBoundsState, PhonePipMenuController menuController,
             PipSnapAlgorithm pipSnapAlgorithm,
             FloatingContentCoordinator floatingContentCoordinator,
-            Optional<PipPerfHintController> pipPerfHintControllerOptional) {
+            PipScheduler pipScheduler,
+            Optional<PipPerfHintController> pipPerfHintControllerOptional,
+            PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipTransitionState pipTransitionState) {
         return new PipMotionHelper(context, pipBoundsState, menuController, pipSnapAlgorithm,
-                floatingContentCoordinator, pipPerfHintControllerOptional);
+                floatingContentCoordinator, pipScheduler, pipPerfHintControllerOptional,
+                pipBoundsAlgorithm, pipTransitionState);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 9038aaa..0b7a3e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -39,6 +39,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.shared.TransitionUtil
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 2d508b2..7e0234e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -48,7 +48,6 @@
         val activeTasks: ArraySet<Int> = ArraySet(),
         val visibleTasks: ArraySet<Int> = ArraySet(),
         val minimizedTasks: ArraySet<Int> = ArraySet(),
-        var stashed: Boolean = false
     )
 
     // Token of the current wallpaper activity, used to remove it when the last task is removed
@@ -95,10 +94,8 @@
         visibleTasksListeners[visibleTasksListener] = executor
         displayData.keyIterator().forEach { displayId ->
             val visibleTasksCount = getVisibleTaskCount(displayId)
-            val stashed = isStashed(displayId)
             executor.execute {
                 visibleTasksListener.onTasksVisibilityChanged(displayId, visibleTasksCount)
-                visibleTasksListener.onStashedChanged(displayId, stashed)
             }
         }
     }
@@ -400,26 +397,6 @@
     }
 
     /**
-     * Update stashed status on display with id [displayId]
-     */
-    fun setStashed(displayId: Int, stashed: Boolean) {
-        val data = displayData.getOrCreate(displayId)
-        val oldValue = data.stashed
-        data.stashed = stashed
-        if (oldValue != stashed) {
-            KtProtoLog.d(
-                    WM_SHELL_DESKTOP_MODE,
-                    "DesktopTaskRepo: mark stashed=%b displayId=%d",
-                    stashed,
-                    displayId
-            )
-            visibleTasksListeners.forEach { (listener, executor) ->
-                executor.execute { listener.onStashedChanged(displayId, stashed) }
-            }
-        }
-    }
-
-    /**
      * Removes and returns the bounds saved before maximizing the given task.
      */
     fun removeBoundsBeforeMaximize(taskId: Int): Rect? {
@@ -433,13 +410,6 @@
         boundsBeforeMaximizeByTaskId.set(taskId, Rect(bounds))
     }
 
-    /**
-     * Check if display with id [displayId] has desktop tasks stashed
-     */
-    fun isStashed(displayId: Int): Boolean {
-        return displayData[displayId]?.stashed ?: false
-    }
-
     internal fun dump(pw: PrintWriter, prefix: String) {
         val innerPrefix = "$prefix  "
         pw.println("${prefix}DesktopModeTaskRepository")
@@ -455,7 +425,6 @@
             pw.println("${prefix}Display $displayId:")
             pw.println("${innerPrefix}activeTasks=${data.activeTasks.toDumpString()}")
             pw.println("${innerPrefix}visibleTasks=${data.visibleTasks.toDumpString()}")
-            pw.println("${innerPrefix}stashed=${data.stashed}")
         }
     }
 
@@ -477,11 +446,6 @@
          * Called when the desktop changes the number of visible freeform tasks.
          */
         fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {}
-
-        /**
-         * Called when the desktop stashed status changes.
-         */
-        fun onStashedChanged(displayId: Int, stashed: Boolean) {}
     }
 }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
new file mode 100644
index 0000000..aa11a7d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.util.Log
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.wm.shell.dagger.WMSingleton
+import javax.inject.Inject
+
+/**
+ * Log Aster UIEvents for desktop windowing mode.
+ */
+@WMSingleton
+class DesktopModeUiEventLogger @Inject constructor(
+    private val mUiEventLogger: UiEventLogger,
+    private val mInstanceIdSequence: InstanceIdSequence
+) {
+    /**
+     * Logs an event for a CUI, on a particular package.
+     *
+     * @param uid The user id associated with the package the user is interacting with
+     * @param packageName The name of the package the user is interacting with
+     * @param event The event type to generate
+     */
+    fun log(uid: Int, packageName: String, event: DesktopUiEventEnum) {
+        if (packageName.isEmpty() || uid < 0) {
+            Log.d(TAG, "Skip logging since package name is empty or bad uid")
+            return
+        }
+        mUiEventLogger.log(event, uid, packageName)
+    }
+
+    /**
+     * Retrieves a new instance id for a new interaction.
+     */
+    fun getNewInstanceId(): InstanceId = mInstanceIdSequence.newInstanceId()
+
+    /**
+     * Logs an event as part of a particular CUI, on a particular package.
+     *
+     * @param instanceId The id identifying an interaction, potentially taking place across multiple
+     * surfaces. There should be a new id generated for each distinct CUI.
+     * @param uid The user id associated with the package the user is interacting with
+     * @param packageName The name of the package the user is interacting with
+     * @param event The event type to generate
+     */
+    fun logWithInstanceId(
+        instanceId: InstanceId,
+        uid: Int,
+        packageName: String,
+        event: DesktopUiEventEnum
+    ) {
+        if (packageName.isEmpty() || uid < 0) {
+            Log.d(TAG, "Skip logging since package name is empty or bad uid")
+            return
+        }
+        mUiEventLogger.logWithInstanceId(event, uid, packageName, instanceId)
+    }
+
+    companion object {
+        /**
+         * Enums for logging desktop windowing mode UiEvents.
+         */
+        enum class DesktopUiEventEnum(private val mId: Int) : UiEventLogger.UiEventEnum {
+
+            @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the edge")
+            DESKTOP_WINDOW_EDGE_DRAG_RESIZE(1721),
+
+            @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the corner")
+            DESKTOP_WINDOW_CORNER_DRAG_RESIZE(1722),
+
+            @UiEvent(doc = "Tap on the window header maximize button in desktop windowing mode")
+            DESKTOP_WINDOW_MAXIMIZE_BUTTON_TAP(1723),
+
+            @UiEvent(doc = "Double tap on window header to maximize it in desktop windowing mode")
+            DESKTOP_WINDOW_HEADER_DOUBLE_TAP_TO_MAXIMIZE(1724);
+
+            override fun getId(): Int = mId
+        }
+
+        private const val TAG = "DesktopModeUiEventLogger"
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index b0d5923..2dc4573 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -72,6 +72,7 @@
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.shared.annotations.ExternalThread
 import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.splitscreen.SplitScreenController
@@ -227,7 +228,7 @@
         bringDesktopAppsToFront(displayId, wct)
 
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            // TODO(b/255649902): ensure remote transition is supplied once state is introduced
+            // TODO(b/309014605): ensure remote transition is supplied once state is introduced
             val transitionType = if (remoteTransition == null) TRANSIT_NONE else TRANSIT_TO_FRONT
             val handler = remoteTransition?.let {
                 OneShotRemoteHandler(transitions.mainExecutor, remoteTransition)
@@ -240,34 +241,6 @@
         }
     }
 
-    /**
-     * Stash desktop tasks on display with id [displayId].
-     *
-     * When desktop tasks are stashed, launcher home screen icons are fully visible. New apps
-     * launched in this state will be added to the desktop. Existing desktop tasks will be brought
-     * back to front during the launch.
-     */
-    fun stashDesktopApps(displayId: Int) {
-        if (DesktopModeStatus.isStashingEnabled()) {
-            KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: stashDesktopApps")
-            desktopModeTaskRepository.setStashed(displayId, true)
-        }
-    }
-
-    /**
-     * Clear the stashed state for the given display
-     */
-    fun hideStashedDesktopApps(displayId: Int) {
-        if (DesktopModeStatus.isStashingEnabled()) {
-            KtProtoLog.v(
-                    WM_SHELL_DESKTOP_MODE,
-                    "DesktopTasksController: hideStashedApps displayId=%d",
-                    displayId
-            )
-            desktopModeTaskRepository.setStashed(displayId, false)
-        }
-    }
-
     /** Get number of tasks that are marked as visible */
     fun getVisibleTaskCount(displayId: Int): Int {
         return desktopModeTaskRepository.getVisibleTaskCount(displayId)
@@ -871,8 +844,6 @@
         val result = triggerTask?.let { task ->
             when {
                 request.type == TRANSIT_TO_BACK -> handleBackNavigation(task)
-                // If display has tasks stashed, handle as stashed launch
-                task.isStashed -> handleStashedTaskLaunch(task, transition)
                 // Check if the task has a top transparent activity
                 shouldLaunchAsModal(task) -> handleTransparentTaskLaunch(task)
                 // Check if fullscreen task should be updated
@@ -911,12 +882,8 @@
                 .forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
     }
 
-    private val TaskInfo.isStashed: Boolean
-        get() = desktopModeTaskRepository.isStashed(displayId)
-
-    private fun shouldLaunchAsModal(task: TaskInfo): Boolean {
-        return Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)
-    }
+    private fun shouldLaunchAsModal(task: TaskInfo) =
+        Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)
 
     private fun shouldRemoveWallpaper(request: TransitionRequestInfo): Boolean {
         return Flags.enableDesktopWindowingWallpaperActivity() &&
@@ -976,24 +943,6 @@
         return null
     }
 
-    private fun handleStashedTaskLaunch(
-            task: RunningTaskInfo,
-            transition: IBinder
-    ): WindowContainerTransaction {
-        KtProtoLog.d(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTasksController: launch apps with stashed on transition taskId=%d",
-                task.taskId
-        )
-        val wct = WindowContainerTransaction()
-        val taskToMinimize =
-                bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
-        addMoveToDesktopChanges(wct, task)
-        desktopModeTaskRepository.setStashed(task.displayId, false)
-        addPendingMinimizeTransition(transition, taskToMinimize)
-        return wct
-    }
-
     // Always launch transparent tasks in fullscreen.
     private fun handleTransparentTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
         // Already fullscreen, no-op.
@@ -1426,16 +1375,6 @@
                     l -> l.onTasksVisibilityChanged(displayId, visibleTasksCount)
                 }
             }
-
-            override fun onStashedChanged(displayId: Int, stashed: Boolean) {
-                KtProtoLog.v(
-                        WM_SHELL_DESKTOP_MODE,
-                        "IDesktopModeImpl: onStashedChanged display=%d stashed=%b",
-                        displayId,
-                        stashed
-                )
-                remoteListener.call { l -> l.onStashedChanged(displayId, stashed) }
-            }
         }
 
         init {
@@ -1467,20 +1406,6 @@
             ) { c -> c.showDesktopApps(displayId, remoteTransition) }
         }
 
-        override fun stashDesktopApps(displayId: Int) {
-            ExecutorUtils.executeRemoteCallWithTaskPermission(
-                    controller,
-                    "stashDesktopApps"
-            ) { c -> c.stashDesktopApps(displayId) }
-        }
-
-        override fun hideStashedDesktopApps(displayId: Int) {
-            ExecutorUtils.executeRemoteCallWithTaskPermission(
-                    controller,
-                    "hideStashedDesktopApps"
-            ) { c -> c.hideStashedDesktopApps(displayId) }
-        }
-
         override fun showDesktopApp(taskId: Int) {
             ExecutorUtils.executeRemoteCallWithTaskPermission(
                     controller,
@@ -1488,6 +1413,20 @@
             ) { c -> c.moveTaskToFront(taskId) }
         }
 
+        override fun stashDesktopApps(displayId: Int) {
+            KtProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "IDesktopModeImpl: stashDesktopApps is deprecated"
+            )
+        }
+
+        override fun hideStashedDesktopApps(displayId: Int) {
+            KtProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "IDesktopModeImpl: hideStashedDesktopApps is deprecated"
+            )
+        }
+
         override fun getVisibleTaskCount(displayId: Int): Int {
             val result = IntArray(1)
             ExecutorUtils.executeRemoteCallWithTaskPermission(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 3404d37..0f88384 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -25,6 +25,7 @@
 import androidx.annotation.VisibleForTesting
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.TransitionObserver
 import com.android.wm.shell.util.KtProtoLog
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index 451e09c..dae75f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -23,6 +23,7 @@
 import android.window.TransitionInfo
 import com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.util.KtProtoLog
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index fa43522..c36f8de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -28,10 +28,10 @@
     /** Show apps on the desktop on the given display */
     void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
 
-    /** Stash apps on the desktop to allow launching another app from home screen */
+    /** @deprecated use {@link #showDesktopApps} instead. */
     void stashDesktopApps(int displayId);
 
-    /** Hide apps that may be stashed */
+    /** @deprecated this is no longer supported. */
     void hideStashedDesktopApps(int displayId);
 
     /** Bring task with the given id to front */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
index 8ed87f2..8ebdfdc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
@@ -25,6 +25,6 @@
     /** Desktop tasks visibility has changed. Visible if at least 1 task is visible. */
     oneway void onTasksVisibilityChanged(int displayId, int visibleTasksCount);
 
-    /** Desktop task stashed status has changed. */
+    /** @deprecated this is no longer supported. */
     oneway void onStashedChanged(int displayId, boolean stashed);
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index ecb53dc..59d6969 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -16,11 +16,11 @@
 
 package com.android.wm.shell.draganddrop;
 
-import static android.app.StatusBarManager.DISABLE2_NONE;
 import static android.app.StatusBarManager.DISABLE_NONE;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS;
 import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -516,20 +516,18 @@
     }
 
     private void animateFullscreenContainer(boolean visible) {
-        int flags = visible ? HIDE_STATUS_BAR_FLAGS : DISABLE_NONE;
-        StatusBarManager.DisableInfo disableInfo = new StatusBarManager.DisableInfo(flags,
-                DISABLE2_NONE);
-        mStatusBarManager.requestDisabledComponent(disableInfo, "animateFullscreenContainer");
+        mStatusBarManager.disable(visible
+                ? HIDE_STATUS_BAR_FLAGS
+                : DISABLE_NONE);
         // We're only using the first drop zone if there is one fullscreen target
         mDropZoneView1.setShowingMargin(visible);
         mDropZoneView1.setShowingHighlight(visible);
     }
 
     private void animateSplitContainers(boolean visible, Runnable animCompleteCallback) {
-        int flags = visible ? HIDE_STATUS_BAR_FLAGS : DISABLE_NONE;
-        StatusBarManager.DisableInfo disableInfo = new StatusBarManager.DisableInfo(flags,
-                DISABLE2_NONE);
-        mStatusBarManager.requestDisabledComponent(disableInfo, "animateSplitContainers");
+        mStatusBarManager.disable(visible
+                ? HIDE_STATUS_BAR_FLAGS
+                : DISABLE_NONE);
         mDropZoneView1.setShowingMargin(visible);
         mDropZoneView2.setShowingMargin(visible);
         Animator animator = mDropZoneView1.getAnimator();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index a414a55..e0e2e706 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -27,9 +27,9 @@
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 9eaf7e4..c79eef7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
 import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING;
@@ -83,6 +84,7 @@
      * @see KeyguardTransitions
      */
     private IRemoteTransition mExitTransition = null;
+    private IRemoteTransition mAppearTransition = null;
     private IRemoteTransition mOccludeTransition = null;
     private IRemoteTransition mOccludeByDreamTransition = null;
     private IRemoteTransition mUnoccludeTransition = null;
@@ -170,26 +172,28 @@
 
         // Choose a transition applicable for the changes and keyguard state.
         if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
-            return startAnimation(mExitTransition,
-                    "going-away",
+            return startAnimation(mExitTransition, "going-away",
                     transition, info, startTransaction, finishTransaction, finishCallback);
         }
 
+        if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0) {
+            return startAnimation(mAppearTransition, "appearing",
+                    transition, info, startTransaction, finishTransaction, finishCallback);
+        }
+
+
         // Occlude/unocclude animations are only played if the keyguard is locked.
         if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
             if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0) {
                 if (hasOpeningDream(info)) {
-                    return startAnimation(mOccludeByDreamTransition,
-                            "occlude-by-dream",
+                    return startAnimation(mOccludeByDreamTransition, "occlude-by-dream",
                             transition, info, startTransaction, finishTransaction, finishCallback);
                 } else {
-                    return startAnimation(mOccludeTransition,
-                            "occlude",
+                    return startAnimation(mOccludeTransition, "occlude",
                             transition, info, startTransaction, finishTransaction, finishCallback);
                 }
             } else if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
-                return startAnimation(mUnoccludeTransition,
-                        "unocclude",
+                return startAnimation(mUnoccludeTransition, "unocclude",
                         transition, info, startTransaction, finishTransaction, finishCallback);
             }
         }
@@ -359,11 +363,13 @@
         @Override
         public void register(
                 IRemoteTransition exitTransition,
+                IRemoteTransition appearTransition,
                 IRemoteTransition occludeTransition,
                 IRemoteTransition occludeByDreamTransition,
                 IRemoteTransition unoccludeTransition) {
             mMainExecutor.execute(() -> {
                 mExitTransition = exitTransition;
+                mAppearTransition = appearTransition;
                 mOccludeTransition = occludeTransition;
                 mOccludeByDreamTransition = occludeByDreamTransition;
                 mUnoccludeTransition = unoccludeTransition;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
index 4215b2c..b7245b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
@@ -35,6 +35,7 @@
      */
     default void register(
             @NonNull IRemoteTransition unlockTransition,
+            @NonNull IRemoteTransition appearTransition,
             @NonNull IRemoteTransition occludeTransition,
             @NonNull IRemoteTransition occludeByDreamTransition,
             @NonNull IRemoteTransition unoccludeTransition) {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 7b1ef5c..a749019 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -39,7 +39,7 @@
      * @param isSysUiStateValid Is SysUI state valid or not.
      * @param flag Current SysUI state.
      */
-    default void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) {
+    default void onSystemUiStateChanged(boolean isSysUiStateValid, long flag) {
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index e885262..e1657f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -854,7 +854,8 @@
         mPipUiEventLoggerLogger.log(uiEventEnum);
 
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                "onTaskAppeared: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState);
+                "onTaskAppeared: %s, state=%s, taskId=%s", mTaskInfo.topActivity,
+                mPipTransitionState, mTaskInfo.taskId);
         if (mPipTransitionState.getInSwipePipToHomeTransition()) {
             if (!mWaitForFixedRotation) {
                 onEndOfSwipePipToHomeTransition();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index fdde3ee..2082756 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -824,12 +824,10 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback,
             @NonNull TaskInfo taskInfo) {
         startTransaction.apply();
-        if (info.getChanges().isEmpty()) {
+        final TransitionInfo.Change pipChange = findCurrentPipTaskChange(info);
+        if (pipChange == null) {
             ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                    "removePipImmediately is called with empty changes");
-        } else {
-            finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
-                    mPipDisplayLayoutState.getDisplayBounds());
+                    "removePipImmediately is called without pip change");
         }
         mPipOrganizer.onExitPipFinished(taskInfo);
         finishCallback.onTransitionFinished(null);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 139cde2..85f9194 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -847,7 +847,7 @@
         }
     }
 
-    private void onSystemUiStateChanged(boolean isValidState, int flag) {
+    private void onSystemUiStateChanged(boolean isValidState, long flag) {
         mTouchHandler.onSystemUiStateChanged(isValidState);
     }
 
@@ -1195,7 +1195,7 @@
         }
 
         @Override
-        public void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) {
+        public void onSystemUiStateChanged(boolean isSysUiStateValid, long flag) {
             mMainExecutor.execute(() -> {
                 PipController.this.onSystemUiStateChanged(isSysUiStateValid, flag);
             });
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
index 03547a5..b757b00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
@@ -53,7 +53,7 @@
      * Listener interface for callers to learn when this class is registered or unregistered with
      * window manager
      */
-    private interface RegistrationListener {
+    interface RegistrationListener {
         void onRegistrationChanged(boolean isRegistered);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index 619bed4..be10151 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -31,7 +31,9 @@
 import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.Debug;
+import android.view.SurfaceControl;
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.R;
@@ -39,6 +41,7 @@
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipPerfHintController;
 import com.android.wm.shell.common.pip.PipSnapAlgorithm;
@@ -55,8 +58,10 @@
  * A helper to animate and manipulate the PiP.
  */
 public class PipMotionHelper implements PipAppOpsListener.Callback,
-        FloatingContentCoordinator.FloatingContent {
+        FloatingContentCoordinator.FloatingContent,
+        PipTransitionState.PipTransitionStateChangedListener {
     private static final String TAG = "PipMotionHelper";
+    private static final String FLING_BOUNDS_CHANGE = "fling_bounds_change";
     private static final boolean DEBUG = false;
 
     private static final int SHRINK_STACK_FROM_MENU_DURATION = 250;
@@ -72,7 +77,9 @@
 
     private final Context mContext;
     private @NonNull PipBoundsState mPipBoundsState;
-
+    private @NonNull PipBoundsAlgorithm mPipBoundsAlgorithm;
+    private @NonNull PipScheduler mPipScheduler;
+    private @NonNull PipTransitionState mPipTransitionState;
     private PhonePipMenuController mMenuController;
     private PipSnapAlgorithm mSnapAlgorithm;
 
@@ -145,6 +152,11 @@
     private boolean mDismissalPending = false;
 
     /**
+     * Set to true if bounds change transition has been scheduled from PipMotionHelper.
+     */
+    private boolean mWaitingForBoundsChangeTransition = false;
+
+    /**
      * Gets set in {@link #animateToExpandedState(Rect, Rect, Rect, Runnable)}, this callback is
      * used to show menu activity when the expand animation is completed.
      */
@@ -152,22 +164,25 @@
 
     public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
             PhonePipMenuController menuController, PipSnapAlgorithm snapAlgorithm,
-            FloatingContentCoordinator floatingContentCoordinator,
-            Optional<PipPerfHintController> pipPerfHintControllerOptional) {
+            FloatingContentCoordinator floatingContentCoordinator, PipScheduler pipScheduler,
+            Optional<PipPerfHintController> pipPerfHintControllerOptional,
+            PipBoundsAlgorithm pipBoundsAlgorithm, PipTransitionState pipTransitionState) {
         mContext = context;
         mPipBoundsState = pipBoundsState;
+        mPipBoundsAlgorithm = pipBoundsAlgorithm;
+        mPipScheduler = pipScheduler;
         mMenuController = menuController;
         mSnapAlgorithm = snapAlgorithm;
         mFloatingContentCoordinator = floatingContentCoordinator;
         mPipPerfHintController = pipPerfHintControllerOptional.orElse(null);
         mResizePipUpdateListener = (target, values) -> {
             if (mPipBoundsState.getMotionBoundsState().isInMotion()) {
-                /*
-                mPipTaskOrganizer.scheduleUserResizePip(getBounds(),
-                        mPipBoundsState.getMotionBoundsState().getBoundsInMotion(), null);
-                 */
+                mPipScheduler.scheduleUserResizePip(
+                        mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
             }
         };
+        mPipTransitionState = pipTransitionState;
+        mPipTransitionState.addPipTransitionStateChangedListener(this);
     }
 
     void init() {
@@ -236,12 +251,7 @@
                 mPipBoundsState.setBounds(toBounds);
             } else {
                 mPipBoundsState.getMotionBoundsState().setBoundsInMotion(toBounds);
-                /*
-                mPipTaskOrganizer.scheduleUserResizePip(getBounds(), toBounds,
-                        (Rect newBounds) -> {
-                                mMenuController.updateMenuLayout(newBounds);
-                        });
-                 */
+                mPipScheduler.scheduleUserResizePip(toBounds);
             }
         } else {
             // If PIP is 'catching up' after being stuck in the dismiss target, update the animation
@@ -552,11 +562,11 @@
     /** Set new fling configs whose min/max values respect the given movement bounds. */
     private void rebuildFlingConfigs() {
         mFlingConfigX = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
-                mPipBoundsState.getMovementBounds().left,
-                mPipBoundsState.getMovementBounds().right);
+                mPipBoundsAlgorithm.getMovementBounds(getBounds()).left,
+                mPipBoundsAlgorithm.getMovementBounds(getBounds()).right);
         mFlingConfigY = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
-                mPipBoundsState.getMovementBounds().top,
-                mPipBoundsState.getMovementBounds().bottom);
+                mPipBoundsAlgorithm.getMovementBounds(getBounds()).top,
+                mPipBoundsAlgorithm.getMovementBounds(getBounds()).bottom);
         final Rect insetBounds = mPipBoundsState.getDisplayLayout().stableInsets();
         mStashConfigX = new PhysicsAnimator.FlingConfig(
                 DEFAULT_FRICTION,
@@ -623,22 +633,15 @@
     private void onBoundsPhysicsAnimationEnd() {
         // The physics animation ended, though we may not necessarily be done animating, such as
         // when we're still dragging after moving out of the magnetic target.
-        if (!mDismissalPending
-                && !mSpringingToTouch
-                && !mMagnetizedPip.getObjectStuckToTarget()) {
-            // All motion operations have actually finished.
-            mPipBoundsState.setBounds(
-                    mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
-            mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded();
-            if (!mDismissalPending) {
-                // do not schedule resize if PiP is dismissing, which may cause app re-open to
-                // mBounds instead of its normal bounds.
-                // mPipTaskOrganizer.scheduleFinishResizePip(getBounds());
-            }
+        if (!mDismissalPending && !mSpringingToTouch && !mMagnetizedPip.getObjectStuckToTarget()) {
+            // do not schedule resize if PiP is dismissing, which may cause app re-open to
+            // mBounds instead of its normal bounds.
+            Bundle extra = new Bundle();
+            extra.putBoolean(FLING_BOUNDS_CHANGE, true);
+            mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
+            return;
         }
-        mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded();
-        mSpringingToTouch = false;
-        mDismissalPending = false;
+        settlePipBoundsAfterPhysicsAnimation(true /* animatingAfter */);
         cleanUpHighPerfSessionMaybe();
     }
 
@@ -662,7 +665,7 @@
                             + " callers=\n%s", TAG, toBounds, Debug.getCallers(5, "    "));
         }
         if (!toBounds.equals(getBounds())) {
-            // mPipTaskOrganizer.scheduleResizePip(toBounds, mUpdateBoundsCallback);
+            mPipScheduler.scheduleAnimateResizePip(toBounds);
         }
     }
 
@@ -685,6 +688,61 @@
         // setAnimatingToBounds(toBounds);
     }
 
+    @Override
+    public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+            @PipTransitionState.TransitionState int newState,
+            @Nullable Bundle extra) {
+        switch (newState) {
+            case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
+                if (!extra.getBoolean(FLING_BOUNDS_CHANGE)) break;
+
+                // If touch is turned off and we are in a fling animation, schedule a transition.
+                mWaitingForBoundsChangeTransition = true;
+                mPipScheduler.scheduleAnimateResizePip(
+                        mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
+                break;
+            case PipTransitionState.CHANGING_PIP_BOUNDS:
+                if (!mWaitingForBoundsChangeTransition) break;
+
+                // If bounds change transition was scheduled from this class, handle leash updates.
+                mWaitingForBoundsChangeTransition = false;
+                SurfaceControl.Transaction startTx = extra.getParcelable(
+                        PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
+                Rect destinationBounds = extra.getParcelable(
+                        PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
+                startTx.setPosition(mPipTransitionState.mPinnedTaskLeash,
+                        destinationBounds.left, destinationBounds.top);
+                startTx.apply();
+
+                // All motion operations have actually finished, so make bounds cache updates.
+                settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
+                cleanUpHighPerfSessionMaybe();
+
+                // Setting state to CHANGED_PIP_BOUNDS applies finishTx and notifies Core.
+                mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS);
+                break;
+            case PipTransitionState.EXITING_PIP:
+                // We need to force finish any local animators if about to leave PiP, to avoid
+                // breaking the state (e.g. leashes are cleaned up upon exit).
+                if (!mPipBoundsState.getMotionBoundsState().isInMotion()) break;
+                cancelPhysicsAnimation();
+                settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
+        }
+    }
+
+    private void settlePipBoundsAfterPhysicsAnimation(boolean animatingAfter) {
+        if (!animatingAfter) {
+            // The physics animation ended, though we may not necessarily be done animating, such as
+            // when we're still dragging after moving out of the magnetic target. Only set the final
+            // bounds state and clear motion bounds completely if the whole animation is over.
+            mPipBoundsState.setBounds(mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
+            mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded();
+        }
+        mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded();
+        mSpringingToTouch = false;
+        mDismissalPending = false;
+    }
+
     /**
      * Returns a MagnetizedObject wrapper for PIP's animated bounds. This is provided to the
      * magnetic dismiss target so it can calculate PIP's size and position.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index 04cf350..b55a41d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -24,6 +24,7 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.hardware.input.InputManager;
+import android.os.Bundle;
 import android.os.Looper;
 import android.view.BatchedInputEventReceiver;
 import android.view.Choreographer;
@@ -32,6 +33,7 @@
 import android.view.InputEventReceiver;
 import android.view.InputMonitor;
 import android.view.MotionEvent;
+import android.view.SurfaceControl;
 import android.view.ViewConfiguration;
 
 import androidx.annotation.VisibleForTesting;
@@ -51,16 +53,20 @@
  * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
  * trigger dynamic resize.
  */
-public class PipResizeGestureHandler {
+public class PipResizeGestureHandler implements
+        PipTransitionState.PipTransitionStateChangedListener {
 
     private static final String TAG = "PipResizeGestureHandler";
     private static final int PINCH_RESIZE_SNAP_DURATION = 250;
     private static final float PINCH_RESIZE_AUTO_MAX_RATIO = 0.9f;
+    private static final String RESIZE_BOUNDS_CHANGE = "resize_bounds_change";
 
     private final Context mContext;
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
     private final PipBoundsState mPipBoundsState;
     private final PipTouchState mPipTouchState;
+    private final PipScheduler mPipScheduler;
+    private final PipTransitionState mPipTransitionState;
     private final PhonePipMenuController mPhonePipMenuController;
     private final PipUiEventLogger mPipUiEventLogger;
     private final PipPinchResizingAlgorithm mPinchResizingAlgorithm;
@@ -88,6 +94,7 @@
     private boolean mIsSysUiStateValid;
     private boolean mThresholdCrossed;
     private boolean mOngoingPinchToResize = false;
+    private boolean mWaitingForBoundsChangeTransition = false;
     private float mAngle = 0;
     int mFirstIndex = -1;
     int mSecondIndex = -1;
@@ -104,11 +111,17 @@
     private int mCtrlType;
     private int mOhmOffset;
 
-    public PipResizeGestureHandler(Context context, PipBoundsAlgorithm pipBoundsAlgorithm,
-            PipBoundsState pipBoundsState, PipTouchState pipTouchState,
+    public PipResizeGestureHandler(Context context,
+            PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipBoundsState pipBoundsState,
+            PipTouchState pipTouchState,
+            PipScheduler pipScheduler,
+            PipTransitionState pipTransitionState,
             Runnable updateMovementBoundsRunnable,
-            PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController,
-            ShellExecutor mainExecutor, @Nullable PipPerfHintController pipPerfHintController) {
+            PipUiEventLogger pipUiEventLogger,
+            PhonePipMenuController menuActivityController,
+            ShellExecutor mainExecutor,
+            @Nullable PipPerfHintController pipPerfHintController) {
         mContext = context;
         mDisplayId = context.getDisplayId();
         mMainExecutor = mainExecutor;
@@ -116,6 +129,11 @@
         mPipBoundsAlgorithm = pipBoundsAlgorithm;
         mPipBoundsState = pipBoundsState;
         mPipTouchState = pipTouchState;
+        mPipScheduler = pipScheduler;
+
+        mPipTransitionState = pipTransitionState;
+        mPipTransitionState.addPipTransitionStateChangedListener(this);
+
         mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
         mPhonePipMenuController = menuActivityController;
         mPipUiEventLogger = pipUiEventLogger;
@@ -125,6 +143,7 @@
             mUserResizeBounds.set(rect);
             // mMotionHelper.synchronizePinnedStackBounds();
             mUpdateMovementBoundsRunnable.run();
+            mPipBoundsState.setBounds(rect);
             resetState();
         };
     }
@@ -202,7 +221,7 @@
     @VisibleForTesting
     void onInputEvent(InputEvent ev) {
         if (!mEnablePinchResize) {
-            // No need to handle anything if neither form of resizing is enabled.
+            // No need to handle anything if resizing isn't enabled.
             return;
         }
 
@@ -227,7 +246,7 @@
                 }
             }
 
-            if (mEnablePinchResize && mOngoingPinchToResize) {
+            if (mOngoingPinchToResize) {
                 onPinchResize(mv);
             }
         }
@@ -249,13 +268,11 @@
     }
 
     boolean willStartResizeGesture(MotionEvent ev) {
-        if (isInValidSysUiState()) {
-            if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
-                if (mEnablePinchResize && ev.getPointerCount() == 2) {
-                    onPinchResize(ev);
-                    mOngoingPinchToResize = mAllowGesture;
-                    return mAllowGesture;
-                }
+        if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
+            if (mEnablePinchResize && ev.getPointerCount() == 2) {
+                onPinchResize(ev);
+                mOngoingPinchToResize = mAllowGesture;
+                return mAllowGesture;
             }
         }
         return false;
@@ -284,7 +301,6 @@
             mSecondIndex = -1;
             mAllowGesture = false;
             finishResize();
-            cleanUpHighPerfSessionMaybe();
         }
 
         if (ev.getPointerCount() != 2) {
@@ -347,10 +363,7 @@
                         mDownSecondPoint, mLastPoint, mLastSecondPoint, mMinSize, mMaxSize,
                         mDownBounds, mLastResizeBounds);
 
-                /*
-                mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
-                        mAngle, null);
-                 */
+                mPipScheduler.scheduleUserResizePip(mLastResizeBounds, mAngle);
                 mPipBoundsState.setHasUserResizedPip(true);
             }
         }
@@ -399,57 +412,43 @@
     }
 
     private void finishResize() {
-        if (!mLastResizeBounds.isEmpty()) {
-            // Pinch-to-resize needs to re-calculate snap fraction and animate to the snapped
-            // position correctly. Drag-resize does not need to move, so just finalize resize.
-            if (mOngoingPinchToResize) {
-                final Rect startBounds = new Rect(mLastResizeBounds);
-                // If user resize is pretty close to max size, just auto resize to max.
-                if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
-                        || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
-                    resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y);
-                }
-
-                // If user resize is smaller than min size, auto resize to min
-                if (mLastResizeBounds.width() < mMinSize.x
-                        || mLastResizeBounds.height() < mMinSize.y) {
-                    resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y);
-                }
-
-                // get the current movement bounds
-                final Rect movementBounds = mPipBoundsAlgorithm
-                        .getMovementBounds(mLastResizeBounds);
-
-                // snap mLastResizeBounds to the correct edge based on movement bounds
-                snapToMovementBoundsEdge(mLastResizeBounds, movementBounds);
-
-                final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
-                        mLastResizeBounds, movementBounds);
-                mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
-
-                // disable any touch events beyond resizing too
-                mPipTouchState.setAllowInputEvents(false);
-
-                /*
-                mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
-                        PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback, () -> {
-                            // enable touch events
-                            mPipTouchState.setAllowInputEvents(true);
-                        });
-                 */
-            } else {
-                /*
-                mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
-                        TRANSITION_DIRECTION_USER_RESIZE,
-                        mUpdateResizeBoundsCallback);
-                 */
-            }
-            final float magnetRadiusPercent = (float) mLastResizeBounds.width() / mMinSize.x / 2.f;
-            mPipUiEventLogger.log(
-                    PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
-        } else {
+        if (mLastResizeBounds.isEmpty()) {
             resetState();
         }
+        if (!mOngoingPinchToResize) {
+            return;
+        }
+        final Rect startBounds = new Rect(mLastResizeBounds);
+
+        // If user resize is pretty close to max size, just auto resize to max.
+        if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
+                || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
+            resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y);
+        }
+
+        // If user resize is smaller than min size, auto resize to min
+        if (mLastResizeBounds.width() < mMinSize.x
+                || mLastResizeBounds.height() < mMinSize.y) {
+            resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y);
+        }
+
+        // get the current movement bounds
+        final Rect movementBounds = mPipBoundsAlgorithm
+                .getMovementBounds(mLastResizeBounds);
+
+        // snap mLastResizeBounds to the correct edge based on movement bounds
+        snapToMovementBoundsEdge(mLastResizeBounds, movementBounds);
+
+        final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
+                mLastResizeBounds, movementBounds);
+        mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
+
+        // Update the transition state to schedule a resize transition.
+        Bundle extra = new Bundle();
+        extra.putBoolean(RESIZE_BOUNDS_CHANGE, true);
+        mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
+
+        mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
     }
 
     private void resetState() {
@@ -509,6 +508,40 @@
         rect.set(l, t, r, b);
     }
 
+    @Override
+    public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+            @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) {
+        switch (newState) {
+            case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
+                if (!extra.getBoolean(RESIZE_BOUNDS_CHANGE)) break;
+                mWaitingForBoundsChangeTransition = true;
+                mPipScheduler.scheduleAnimateResizePip(mLastResizeBounds);
+                break;
+            case PipTransitionState.CHANGING_PIP_BOUNDS:
+                if (!mWaitingForBoundsChangeTransition) break;
+
+                // If bounds change transition was scheduled from this class, handle leash updates.
+                mWaitingForBoundsChangeTransition = false;
+
+                SurfaceControl.Transaction startTx = extra.getParcelable(
+                        PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
+                Rect destinationBounds = extra.getParcelable(
+                        PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
+                startTx.setPosition(mPipTransitionState.mPinnedTaskLeash,
+                        destinationBounds.left, destinationBounds.top);
+                startTx.apply();
+
+                // All motion operations have actually finished, so make bounds cache updates.
+                cleanUpHighPerfSessionMaybe();
+
+                // Setting state to CHANGED_PIP_BOUNDS applies finishTx and notifies Core.
+                mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS);
+
+                mUpdateResizeBoundsCallback.accept(destinationBounds);
+                break;
+        }
+    }
+
     /**
      * Dumps the {@link PipResizeGestureHandler} state.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 72fa3ba..4947507 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -24,17 +24,21 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.view.SurfaceControl;
 import android.window.WindowContainerTransaction;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.core.content.ContextCompat;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -156,4 +160,39 @@
         wct.setBounds(mPipTransitionState.mPipTaskToken, toBounds);
         mPipTransitionController.startResizeTransition(wct);
     }
+
+    /**
+     * Directly perform a scaled matrix transformation on the leash. This will not perform any
+     * {@link WindowContainerTransaction}.
+     */
+    public void scheduleUserResizePip(Rect toBounds) {
+        scheduleUserResizePip(toBounds, 0f /* degrees */);
+    }
+
+    /**
+     * Directly perform a scaled matrix transformation on the leash. This will not perform any
+     * {@link WindowContainerTransaction}.
+     *
+     * @param degrees the angle to rotate the bounds to.
+     */
+    public void scheduleUserResizePip(Rect toBounds, float degrees) {
+        if (toBounds.isEmpty()) {
+            ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                    "%s: Attempted to user resize PIP to empty bounds, aborting.", TAG);
+            return;
+        }
+        SurfaceControl leash = mPipTransitionState.mPinnedTaskLeash;
+        final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+
+        Matrix transformTensor = new Matrix();
+        final float[] mMatrixTmp = new float[9];
+        final float scale = (float) toBounds.width() / mPipBoundsState.getBounds().width();
+
+        transformTensor.setScale(scale, scale);
+        transformTensor.postTranslate(toBounds.left, toBounds.top);
+        transformTensor.postRotate(degrees, toBounds.centerX(), toBounds.centerY());
+
+        tx.setMatrix(leash, transformTensor, mMatrixTmp);
+        tx.apply();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 472003c..319d199 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.pip2.phone;
 
+import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASH_MINIMUM_VELOCITY_THRESHOLD;
 import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT;
@@ -30,18 +32,19 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.provider.DeviceConfig;
 import android.util.Size;
 import android.view.DisplayCutout;
 import android.view.InputEvent;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
+import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -70,7 +73,7 @@
  * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
  * the PIP.
  */
-public class PipTouchHandler {
+public class PipTouchHandler implements PipTransitionState.PipTransitionStateChangedListener {
 
     private static final String TAG = "PipTouchHandler";
     private static final float DEFAULT_STASH_VELOCITY_THRESHOLD = 18000.f;
@@ -80,6 +83,8 @@
     private final Context mContext;
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
     @NonNull private final PipBoundsState mPipBoundsState;
+    @NonNull private final PipTransitionState mPipTransitionState;
+    @NonNull private final PipScheduler mPipScheduler;
     @NonNull private final SizeSpecSource mSizeSpecSource;
     private final PipUiEventLogger mPipUiEventLogger;
     private final PipDismissTargetHandler mPipDismissTargetHandler;
@@ -125,6 +130,7 @@
     private final FloatingContentCoordinator mFloatingContentCoordinator;
     private PipMotionHelper mMotionHelper;
     private PipTouchGesture mGesture;
+    private PipInputConsumer mPipInputConsumer;
 
     // Temp vars
     private final Rect mTmpBounds = new Rect();
@@ -167,6 +173,8 @@
             PhonePipMenuController menuController,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             @NonNull PipBoundsState pipBoundsState,
+            @NonNull PipTransitionState pipTransitionState,
+            @NonNull PipScheduler pipScheduler,
             @NonNull SizeSpecSource sizeSpecSource,
             PipMotionHelper pipMotionHelper,
             FloatingContentCoordinator floatingContentCoordinator,
@@ -179,6 +187,10 @@
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         mPipBoundsAlgorithm = pipBoundsAlgorithm;
         mPipBoundsState = pipBoundsState;
+
+        mPipTransitionState = pipTransitionState;
+        mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged);
+        mPipScheduler = pipScheduler;
         mSizeSpecSource = sizeSpecSource;
         mMenuController = menuController;
         mPipUiEventLogger = pipUiEventLogger;
@@ -204,10 +216,10 @@
                 },
                 menuController::hideMenu,
                 mainExecutor);
-        mPipResizeGestureHandler =
-                new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
-                        mTouchState, this::updateMovementBounds, pipUiEventLogger,
-                        menuController, mainExecutor, mPipPerfHintController);
+        mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm,
+                pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState,
+                this::updateMovementBounds, pipUiEventLogger, menuController, mainExecutor,
+                mPipPerfHintController);
         mPipBoundsState.addOnAspectRatioChangedCallback(this::updateMinMaxSize);
 
         if (PipUtils.isPip2ExperimentEnabled()) {
@@ -227,6 +239,11 @@
         mPipResizeGestureHandler.init();
         mPipDismissTargetHandler.init();
 
+        mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
+                INPUT_CONSUMER_PIP, mMainExecutor);
+        mPipInputConsumer.setInputListener(this::handleTouchEvent);
+        mPipInputConsumer.setRegistrationListener(this::onRegistrationChanged);
+
         mEnableStash = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 PIP_STASHING,
@@ -294,19 +311,17 @@
 
     void onActivityPinned() {
         mPipDismissTargetHandler.createOrUpdateDismissTarget();
-
         mPipResizeGestureHandler.onActivityPinned();
         mFloatingContentCoordinator.onContentAdded(mMotionHelper);
+        mPipInputConsumer.registerInputConsumer();
     }
 
-    void onActivityUnpinned(ComponentName topPipActivity) {
-        if (topPipActivity == null) {
-            // Clean up state after the last PiP activity is removed
-            mPipDismissTargetHandler.cleanUpDismissTarget();
-
-            mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
-        }
+    void onActivityUnpinned() {
+        // Clean up state after the last PiP activity is removed
+        mPipDismissTargetHandler.cleanUpDismissTarget();
+        mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
         mPipResizeGestureHandler.onActivityUnpinned();
+        mPipInputConsumer.unregisterInputConsumer();
     }
 
     void onPinnedStackAnimationEnded(
@@ -512,6 +527,7 @@
             return true;
         }
 
+        /*
         if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting())
                 && mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) {
             // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
@@ -528,11 +544,13 @@
             return true;
         }
 
-        if (!mTouchState.isUserInteracting()) {
+        // Ignore the motion event When the entry animation is waiting to be started
+        if (!mTouchState.isUserInteracting() && mPipTaskOrganizer.isEntryScheduled()) {
             ProtoLog.wtf(WM_SHELL_PICTURE_IN_PICTURE,
                     "%s: Waiting to start the entry animation, skip the motion event.", TAG);
             return true;
         }
+         */
 
         // Update the touch state
         mTouchState.onTouchEvent(ev);
@@ -808,7 +826,7 @@
             mMovementWithinDismiss = touchState.getDownTouchPosition().y
                     >= mPipBoundsState.getMovementBounds().bottom;
             mMotionHelper.setSpringingToTouch(false);
-            // mPipDismissTargetHandler.setTaskLeash(mPipTaskOrganizer.getSurfaceControl());
+            mPipDismissTargetHandler.setTaskLeash(mPipTransitionState.mPinnedTaskLeash);
 
             // If the menu is still visible then just poke the menu
             // so that it will timeout after the user stops touching it
@@ -880,7 +898,8 @@
                 // Reset the touch state on up before the fling settles
                 mTouchState.reset();
                 if (mEnableStash && shouldStash(vel, getPossiblyMotionBounds())) {
-                    mMotionHelper.stashToEdge(vel.x, vel.y, this::stashEndAction /* endAction */);
+                    // mMotionHelper.stashToEdge(vel.x, vel.y,
+                    //      this::stashEndAction /* endAction */);
                 } else {
                     if (mPipBoundsState.isStashed()) {
                         // Reset stashed state if previously stashed
@@ -1059,6 +1078,28 @@
         mPipResizeGestureHandler.setOhmOffset(offset);
     }
 
+    @Override
+    public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+            @PipTransitionState.TransitionState int newState,
+            @Nullable Bundle extra) {
+        switch (newState) {
+            case PipTransitionState.ENTERED_PIP:
+                onActivityPinned();
+                mTouchState.setAllowInputEvents(true);
+                break;
+            case PipTransitionState.EXITED_PIP:
+                mTouchState.setAllowInputEvents(false);
+                onActivityUnpinned();
+                break;
+            case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
+                mTouchState.setAllowInputEvents(false);
+                break;
+            case PipTransitionState.CHANGED_PIP_BOUNDS:
+                mTouchState.setAllowInputEvents(true);
+                break;
+        }
+    }
+
     /**
      * Dumps the {@link PipTouchHandler} state.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 8b2d0dd..7dddd27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -61,11 +61,15 @@
 public class PipTransition extends PipTransitionController implements
         PipTransitionState.PipTransitionStateChangedListener {
     private static final String TAG = PipTransition.class.getSimpleName();
+
+    // Used when for ENTERING_PIP state update.
     private static final String PIP_TASK_TOKEN = "pip_task_token";
     private static final String PIP_TASK_LEASH = "pip_task_leash";
-    private static final String PIP_START_TX = "pip_start_tx";
-    private static final String PIP_FINISH_TX = "pip_finish_tx";
-    private static final String PIP_DESTINATION_BOUNDS = "pip_dest_bounds";
+
+    // Used for PiP CHANGING_BOUNDS state update.
+    static final String PIP_START_TX = "pip_start_tx";
+    static final String PIP_FINISH_TX = "pip_finish_tx";
+    static final String PIP_DESTINATION_BOUNDS = "pip_dest_bounds";
 
     /**
      * The fixed start delay in ms when fading out the content overlay from bounds animation.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index 9a9c59e2..8204d41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -71,17 +71,21 @@
     // State for app finishing drawing in PiP mode as a final step in enter PiP flow.
     public static final int ENTERED_PIP = 3;
 
-    // State for scheduling a transition to change PiP bounds.
-    public static final int CHANGING_PIP_BOUNDS = 4;
+    // State to indicate we have scheduled a PiP bounds change transition.
+    public static final int SCHEDULED_BOUNDS_CHANGE = 4;
 
-    // State for app potentially finishing drawing in new PiP bounds after resize is complete.
-    public static final int CHANGED_PIP_BOUNDS = 5;
+    // State for the start of playing a transition to change PiP bounds. At this point, WM Core
+    // is aware of the new PiP bounds, but Shell might still be continuing animating.
+    public static final int CHANGING_PIP_BOUNDS = 5;
+
+    // State for finishing animating into new PiP bounds after resize is complete.
+    public static final int CHANGED_PIP_BOUNDS = 6;
 
     // State for starting exiting PiP.
-    public static final int EXITING_PIP = 6;
+    public static final int EXITING_PIP = 7;
 
     // State for finishing exit PiP flow.
-    public static final int EXITED_PIP = 7;
+    public static final int EXITED_PIP = 8;
 
     private static final int FIRST_CUSTOM_STATE = 1000;
 
@@ -92,6 +96,7 @@
             SWIPING_TO_PIP,
             ENTERING_PIP,
             ENTERED_PIP,
+            SCHEDULED_BOUNDS_CHANGE,
             CHANGING_PIP_BOUNDS,
             CHANGED_PIP_BOUNDS,
             EXITING_PIP,
@@ -165,10 +170,11 @@
      * @param extra a bundle passed to the subscribed listeners to resolve/cache extra info.
      */
     public void setState(@TransitionState int state, @Nullable Bundle extra) {
-        if (state == ENTERING_PIP || state == SWIPING_TO_PIP) {
-            // Whenever we are entering PiP caller must provide extra state to set as well.
+        if (state == ENTERING_PIP || state == SWIPING_TO_PIP
+                || state == SCHEDULED_BOUNDS_CHANGE || state == CHANGING_PIP_BOUNDS) {
+            // States listed above require extra bundles to be provided.
             Preconditions.checkArgument(extra != null && !extra.isEmpty(),
-                    "No extra bundle for either ENTERING_PIP or SWIPING_TO_PIP state.");
+                    "No extra bundle for " + stateToString(state) + " state.");
         }
         if (mState != state) {
             dispatchPipTransitionStateChanged(mState, state, extra);
@@ -254,23 +260,24 @@
         return ++mPrevCustomState;
     }
 
-    private String stateToString() {
-        switch (mState) {
+    private static String stateToString(int state) {
+        switch (state) {
             case UNDEFINED: return "undefined";
             case SWIPING_TO_PIP: return "swiping_to_pip";
             case ENTERING_PIP: return "entering-pip";
             case ENTERED_PIP: return "entered-pip";
+            case SCHEDULED_BOUNDS_CHANGE: return "scheduled_bounds_change";
             case CHANGING_PIP_BOUNDS: return "changing-bounds";
             case CHANGED_PIP_BOUNDS: return "changed-bounds";
             case EXITING_PIP: return "exiting-pip";
             case EXITED_PIP: return "exited-pip";
         }
-        throw new IllegalStateException("Unknown state: " + mState);
+        throw new IllegalStateException("Unknown state: " + state);
     }
 
     @Override
     public String toString() {
         return String.format("PipTransitionState(mState=%s, mInSwipePipToHomeTransition=%b)",
-                stateToString(), mInSwipePipToHomeTransition);
+                stateToString(mState), mInSwipePipToHomeTransition);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index a8611d9..c53e7fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -50,9 +50,9 @@
 import com.android.wm.shell.common.SingleInstanceRemoteListener;
 import com.android.wm.shell.common.TaskStackListenerCallback;
 import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.shared.annotations.ExternalThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.sysui.ShellCommandHandler;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index b10176d..8e97068 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -47,6 +47,7 @@
 import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
 import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
+import static com.android.wm.shell.shared.TransitionUtil.isOpeningMode;
 import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
@@ -67,6 +68,7 @@
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
 import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
+import static com.android.wm.shell.transition.MixedTransitionHelper.getPipReplacingChange;
 import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -2836,7 +2838,7 @@
             mSplitLayout.setFreezeDividerWindow(false);
             final StageChangeRecord record = new StageChangeRecord();
             final int transitType = info.getType();
-            boolean hasEnteringPip = false;
+            TransitionInfo.Change pipChange = null;
             for (int iC = 0; iC < info.getChanges().size(); ++iC) {
                 final TransitionInfo.Change change = info.getChanges().get(iC);
                 if (change.getMode() == TRANSIT_CHANGE
@@ -2847,7 +2849,7 @@
                 }
 
                 if (mMixedHandler.isEnteringPip(change, transitType)) {
-                    hasEnteringPip = true;
+                    pipChange = change;
                 }
 
                 final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
@@ -2899,9 +2901,19 @@
                 }
             }
 
-            if (hasEnteringPip) {
+            if (pipChange != null) {
+                TransitionInfo.Change pipReplacingChange = getPipReplacingChange(info, pipChange,
+                        mMainStage.mRootTaskInfo.taskId, mSideStage.mRootTaskInfo.taskId,
+                        getSplitItemStage(pipChange.getLastParent()));
+                if (pipReplacingChange != null) {
+                    // Set an enter transition for when startAnimation gets called again
+                    mSplitTransitions.setEnterTransition(transition, /*remoteTransition*/ null,
+                            TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, /*resizeAnim*/ false);
+                }
+
                 mMixedHandler.animatePendingEnterPipFromSplit(transition, info,
-                        startTransaction, finishTransaction, finishCallback);
+                        startTransaction, finishTransaction, finishCallback,
+                        pipReplacingChange != null);
                 notifySplitAnimationFinished();
                 return true;
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 1e305c5..f77c80d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -177,9 +177,11 @@
     @Override
     @CallSuper
     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
-        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%d taskParent=%d rootTask=%d",
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: taskId=%d taskParent=%d rootTask=%d "
+                        + "taskActivity=%s",
                 taskInfo.taskId, taskInfo.parentTaskId,
-                mRootTaskInfo != null ? mRootTaskInfo.taskId : -1);
+                mRootTaskInfo != null ? mRootTaskInfo.taskId : -1,
+                taskInfo.baseActivity);
         if (mRootTaskInfo == null) {
             mRootLeash = leash;
             mRootTaskInfo = taskInfo;
@@ -213,6 +215,8 @@
     @Override
     @CallSuper
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: taskId=%d taskAct=%s",
+                taskInfo.taskId, taskInfo.baseActivity);
         mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo));
         if (mRootTaskInfo.taskId == taskInfo.taskId) {
             // Inflates split decor view only when the root task is visible.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index e419462..e07e1b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -45,6 +45,7 @@
 
 import com.android.internal.R;
 
+import java.io.Closeable;
 import java.util.function.LongConsumer;
 
 /**
@@ -100,7 +101,7 @@
      * Drawable pre-drawing the scaled icon in a separate thread to increase the speed of the
      * final drawing.
      */
-    private static class ImmobileIconDrawable extends Drawable {
+    private static class ImmobileIconDrawable extends Drawable implements Closeable {
         private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
                 | Paint.FILTER_BITMAP_FLAG);
         private final Matrix mMatrix = new Matrix();
@@ -154,6 +155,16 @@
         public int getOpacity() {
             return 1;
         }
+
+        @Override
+        public void close() {
+            synchronized (mPaint) {
+                if (mIconBitmap != null) {
+                    mIconBitmap.recycle();
+                    mIconBitmap = null;
+                }
+            }
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 968b27b..bcacecb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -77,6 +77,7 @@
     private ActivityEmbeddingController mActivityEmbeddingController;
 
     abstract static class MixedTransition {
+        /** Entering Pip from split, breaks split. */
         static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
 
         /** Both the display and split-state (enter/exit) is changing */
@@ -103,6 +104,9 @@
         /** Enter pip from one of the Activity Embedding windows. */
         static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 9;
 
+        /** Entering Pip from split, but replace the Pip stage instead of breaking split. */
+        static final int TYPE_ENTER_PIP_REPLACE_FROM_SPLIT = 10;
+
         /** The default animation for this mixed transition. */
         static final int ANIM_TYPE_DEFAULT = 0;
 
@@ -484,9 +488,11 @@
     // TODO(b/287704263): Remove when split/mixed are reversed.
     public boolean animatePendingEnterPipFromSplit(IBinder transition, TransitionInfo info,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            Transitions.TransitionFinishCallback finishCallback) {
-        final MixedTransition mixed = createDefaultMixedTransition(
-                MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition);
+            Transitions.TransitionFinishCallback finishCallback, boolean replacingPip) {
+        int type = replacingPip
+                ? MixedTransition.TYPE_ENTER_PIP_REPLACE_FROM_SPLIT
+                : MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT;
+        final MixedTransition mixed = createDefaultMixedTransition(type, transition);
         mActiveTransitions.add(mixed);
         Transitions.TransitionFinishCallback callback = wct -> {
             mActiveTransitions.remove(mixed);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index b028bd6..0ada749 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -76,7 +76,12 @@
                             info, startTransaction, finishTransaction, finishCallback);
             case TYPE_ENTER_PIP_FROM_SPLIT ->
                     animateEnterPipFromSplit(this, info, startTransaction, finishTransaction,
-                            finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler);
+                            finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler,
+                            /*replacingPip*/ false);
+            case TYPE_ENTER_PIP_REPLACE_FROM_SPLIT ->
+                    animateEnterPipFromSplit(this, info, startTransaction, finishTransaction,
+                            finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler,
+                            /*replacingPip*/ true);
             case TYPE_KEYGUARD ->
                     animateKeyguard(this, info, startTransaction, finishTransaction, finishCallback,
                             mKeyguardHandler, mPipHandler);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
index ffc0b76..e8b01b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
@@ -23,11 +23,15 @@
 import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
+import static com.android.wm.shell.shared.TransitionUtil.isOpeningMode;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
 import static com.android.wm.shell.transition.DefaultMixedHandler.subCopy;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 
@@ -45,7 +49,8 @@
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback,
             @NonNull Transitions player, @NonNull MixedTransitionHandler mixedHandler,
-            @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
+            @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler,
+            boolean replacingPip) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
                 + "entering PIP while Split-Screen is foreground.");
         TransitionInfo.Change pipChange = null;
@@ -99,7 +104,7 @@
             // we need a separate one to send over to launcher.
             SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
             @SplitScreen.StageType int topStageToKeep = STAGE_TYPE_UNDEFINED;
-            if (splitHandler.isSplitScreenVisible()) {
+            if (splitHandler.isSplitScreenVisible() && !replacingPip) {
                 // The non-going home case, we could be pip-ing one of the split stages and keep
                 // showing the other
                 for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -115,11 +120,12 @@
                         break;
                     }
                 }
+
+                // Let split update internal state for dismiss.
+                splitHandler.prepareDismissAnimation(topStageToKeep,
+                        EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT,
+                        finishTransaction);
             }
-            // Let split update internal state for dismiss.
-            splitHandler.prepareDismissAnimation(topStageToKeep,
-                    EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT,
-                    finishTransaction);
 
             // We are trying to accommodate launcher's close animation which can't handle the
             // divider-bar, so if split-handler is closing the divider-bar, just hide it and
@@ -152,6 +158,44 @@
         return true;
     }
 
+    /**
+     * Check to see if we're only closing split to enter pip or if we're replacing pip with
+     * another task. If we are replacing, this will return the change for the task we are replacing
+     * pip with
+     *
+     * @param info Any number of changes
+     * @param pipChange TransitionInfo.Change indicating the task that is being pipped
+     * @param splitMainStageRootId MainStage's rootTaskInfo's id
+     * @param splitSideStageRootId SideStage's rootTaskInfo's id
+     * @param lastPipSplitStage The last stage that {@param pipChange} was in
+     * @return The change from {@param info} that is replacing the {@param pipChange}, {@code null}
+     *         otherwise
+     */
+    @Nullable
+    public static TransitionInfo.Change getPipReplacingChange(TransitionInfo info,
+            TransitionInfo.Change pipChange, int splitMainStageRootId, int splitSideStageRootId,
+            @SplitScreen.StageType int lastPipSplitStage) {
+        int lastPipParentTask = -1;
+        if (lastPipSplitStage == STAGE_TYPE_MAIN) {
+            lastPipParentTask = splitMainStageRootId;
+        } else if (lastPipSplitStage == STAGE_TYPE_SIDE) {
+            lastPipParentTask = splitSideStageRootId;
+        }
+
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            TransitionInfo.Change change = info.getChanges().get(i);
+            if (change == pipChange || !isOpeningMode(change.getMode())) {
+                // Ignore the change/task that's going into Pip or not opening
+                continue;
+            }
+
+            if (change.getTaskInfo().parentTaskId == lastPipParentTask) {
+                return change;
+            }
+        }
+        return null;
+    }
+
     private static boolean isHomeOpening(@NonNull TransitionInfo.Change change) {
         return change.getTaskInfo() != null
                 && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index d6e64cf..9fc6702 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -142,7 +142,8 @@
                     && mSplitHandler.getSplitItemPosition(change.getLastParent())
                     != SPLIT_POSITION_UNDEFINED) {
                 return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction,
-                        finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler);
+                        finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler,
+                        /*replacingPip*/ false);
             }
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 4d3c763..6224543 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -31,7 +31,6 @@
 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
 import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
-import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
 import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 
@@ -567,15 +566,15 @@
         final int mode = change.getMode();
         // Put all the OPEN/SHOW on top
         if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
-            if (isOpening
-                    // This is for when an activity launches while a different transition is
-                    // collecting.
-                    || change.hasFlags(FLAG_MOVED_TO_TOP)) {
+            if (isOpening) {
                 // put on top
                 return zSplitLine + numChanges - i;
-            } else {
+            } else if (isClosing) {
                 // put on bottom
                 return zSplitLine - i;
+            } else {
+                // maintain relative ordering (put all changes in the animating layer)
+                return zSplitLine + numChanges - i;
             }
         } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
             if (isOpening) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
index 1897560..6adbe4f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
@@ -49,8 +49,12 @@
 
     public PerfettoTransitionTracer() {
         Producer.init(InitArguments.DEFAULTS);
-        mDataSource.register(
-                new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+        DataSourceParams params =
+                new DataSourceParams.Builder()
+                        .setBufferExhaustedPolicy(
+                                PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+                        .build();
+        mDataSource.register(params);
     }
 
     /**
@@ -214,8 +218,6 @@
 
             }
             os.end(mappingsToken);
-
-            ctx.flush();
         });
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index dfdb58a..9afb057 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -84,12 +84,12 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
 import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
 import com.android.wm.shell.splitscreen.SplitScreenController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 4c347ad..4d4dc3c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -66,8 +66,8 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index de6c035..5418254 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -52,7 +52,7 @@
 
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement;
 
 import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
index 4dd14f4..f69a90c 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
index 5c86a38..b76d065 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
index bc486c2..984abf8 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
@@ -32,7 +32,7 @@
 /**
  * Test launching a new activity from bubble.
  *
- * To run this test: `atest WMShellFlickerTests:MultiBubblesScreen`
+ * To run this test: `atest WMShellFlickerTestsBubbles:ChangeActiveActivityFromBubbleTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
index 2a9b107..886b70c 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
@@ -35,7 +35,7 @@
 /**
  * Test launching a new activity from bubble.
  *
- * To run this test: `atest WMShellFlickerTests:DismissBubbleScreen`
+ * To run this test: `atest WMShellFlickerTestsBubbles:DragToDismissBubbleScreenTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 9ef49c1..2ee53f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -38,7 +38,7 @@
 /**
  * Test launching a new activity from bubble.
  *
- * To run this test: `atest WMShellFlickerTests:OpenActivityFromBubbleOnLocksreenTest`
+ * To run this test: `atest WMShellFlickerTestsBubbles:OpenActivityFromBubbleOnLocksreenTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
index ef7fbfb..463fe0e 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
@@ -29,7 +29,7 @@
 /**
  * Test launching a new activity from bubble.
  *
- * To run this test: `atest WMShellFlickerTests:ExpandBubbleScreen`
+ * To run this test: `atest WMShellFlickerTestsBubbles:OpenActivityFromBubbleTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
index 87224b15..8df5056 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
@@ -29,7 +29,7 @@
 /**
  * Test creating a bubble notification
  *
- * To run this test: `atest WMShellFlickerTests:LaunchBubbleScreen`
+ * To run this test: `atest WMShellFlickerTestsBubbles:SendBubbleNotificationTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
index aa70c09..041978c 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
index c7c804f..a66dfb4 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
index 17cace0..d485b82 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
@@ -21,6 +21,7 @@
 import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways
 import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart
 import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd
+import android.tools.flicker.assertors.assertions.AppWindowIsVisibleAlways
 import android.tools.flicker.assertors.assertions.AppWindowOnTopAtEnd
 import android.tools.flicker.assertors.assertions.AppWindowOnTopAtStart
 import android.tools.flicker.assertors.assertions.AppWindowRemainInsideDisplayBounds
@@ -133,9 +134,8 @@
                     }
                 ),
                 assertions =
-                AssertionTemplates.COMMON_ASSERTIONS +
                         listOf(
-                            AppLayerIsVisibleAlways(Components.DESKTOP_MODE_APP),
+                            AppWindowIsVisibleAlways(Components.DESKTOP_MODE_APP),
                             AppWindowOnTopAtEnd(Components.DESKTOP_MODE_APP),
                             AppWindowRemainInsideDisplayBounds(Components.DESKTOP_MODE_APP),
                         ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
index 214bdfa..85715db 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index f99b4b2..f6f3aa4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -120,7 +120,7 @@
     private TestableContentResolver mContentResolver;
     private TestableLooper mTestableLooper;
 
-    private CrossActivityBackAnimation mCrossActivityBackAnimation;
+    private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation;
     private CrossTaskBackAnimation mCrossTaskBackAnimation;
     private ShellBackAnimationRegistry mShellBackAnimationRegistry;
 
@@ -135,13 +135,14 @@
                 ANIMATION_ENABLED);
         mTestableLooper = TestableLooper.get(this);
         mShellInit = spy(new ShellInit(mShellExecutor));
-        mCrossActivityBackAnimation = new CrossActivityBackAnimation(mContext, mAnimationBackground,
-                mRootTaskDisplayAreaOrganizer);
+        mDefaultCrossActivityBackAnimation = new DefaultCrossActivityBackAnimation(mContext,
+                mAnimationBackground, mRootTaskDisplayAreaOrganizer);
         mCrossTaskBackAnimation = new CrossTaskBackAnimation(mContext, mAnimationBackground);
         mShellBackAnimationRegistry =
-                new ShellBackAnimationRegistry(mCrossActivityBackAnimation, mCrossTaskBackAnimation,
-                        /* dialogCloseAnimation= */ null,
-                        new CustomizeActivityAnimation(mContext, mAnimationBackground),
+                new ShellBackAnimationRegistry(mDefaultCrossActivityBackAnimation,
+                        mCrossTaskBackAnimation, /* dialogCloseAnimation= */ null,
+                        new CustomCrossActivityBackAnimation(mContext, mAnimationBackground,
+                                mRootTaskDisplayAreaOrganizer),
                         /* defaultBackToHomeAnimation= */ null);
         mController =
                 new BackAnimationController(
@@ -582,7 +583,7 @@
     @Test
     public void testBackToActivity() throws RemoteException {
         verifySystemBackBehavior(BackNavigationInfo.TYPE_CROSS_ACTIVITY,
-                mCrossActivityBackAnimation.getRunner());
+                mDefaultCrossActivityBackAnimation.getRunner());
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
new file mode 100644
index 0000000..8bf0111
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -0,0 +1,264 @@
+/*
+ * 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.f
+ */
+package com.android.wm.shell.back
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.AppCompatTaskInfo
+import android.app.WindowConfiguration
+import android.graphics.Color
+import android.graphics.Point
+import android.graphics.Rect
+import android.os.RemoteException
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.Choreographer
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.view.animation.Animation
+import android.window.BackEvent
+import android.window.BackMotionEvent
+import android.window.BackNavigationInfo
+import androidx.test.filters.SmallTest
+import com.android.internal.policy.TransitionAnimation
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import junit.framework.TestCase.assertEquals
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class CustomCrossActivityBackAnimationTest : ShellTestCase() {
+    @Mock private lateinit var backAnimationBackground: BackAnimationBackground
+    @Mock private lateinit var mockCloseAnimation: Animation
+    @Mock private lateinit var mockOpenAnimation: Animation
+    @Mock private lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+    @Mock private lateinit var transitionAnimation: TransitionAnimation
+    @Mock private lateinit var appCompatTaskInfo: AppCompatTaskInfo
+    @Mock private lateinit var transaction: Transaction
+
+    private lateinit var customCrossActivityBackAnimation: CustomCrossActivityBackAnimation
+    private lateinit var customAnimationLoader: CustomAnimationLoader
+
+    @Before
+    @Throws(Exception::class)
+    fun setUp() {
+        customAnimationLoader = CustomAnimationLoader(transitionAnimation)
+        customCrossActivityBackAnimation =
+            CustomCrossActivityBackAnimation(
+                context,
+                backAnimationBackground,
+                rootTaskDisplayAreaOrganizer,
+                transaction,
+                mock(Choreographer::class.java),
+                customAnimationLoader
+            )
+
+        whenever(transitionAnimation.loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(OPEN_RES_ID)))
+            .thenReturn(mockOpenAnimation)
+        whenever(transitionAnimation.loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(CLOSE_RES_ID)))
+            .thenReturn(mockCloseAnimation)
+        whenever(transaction.setColor(any(), any())).thenReturn(transaction)
+        whenever(transaction.setAlpha(any(), anyFloat())).thenReturn(transaction)
+        whenever(transaction.setCrop(any(), any())).thenReturn(transaction)
+        whenever(transaction.setRelativeLayer(any(), any(), anyInt())).thenReturn(transaction)
+        spy(customCrossActivityBackAnimation)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun receiveFinishAfterInvoke() {
+        val finishCalled = startCustomAnimation()
+        try {
+            customCrossActivityBackAnimation.getRunner().callback.onBackInvoked()
+        } catch (r: RemoteException) {
+            Assert.fail("onBackInvoked throw remote exception")
+        }
+        finishCalled.await(1, TimeUnit.SECONDS)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun receiveFinishAfterCancel() {
+        val finishCalled = startCustomAnimation()
+        try {
+            customCrossActivityBackAnimation.getRunner().callback.onBackCancelled()
+        } catch (r: RemoteException) {
+            Assert.fail("onBackCancelled throw remote exception")
+        }
+        finishCalled.await(1, TimeUnit.SECONDS)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun receiveFinishWithoutAnimationAfterInvoke() {
+        val finishCalled = startCustomAnimation(targets = arrayOf())
+        try {
+            customCrossActivityBackAnimation.getRunner().callback.onBackInvoked()
+        } catch (r: RemoteException) {
+            Assert.fail("onBackInvoked throw remote exception")
+        }
+        finishCalled.await(1, TimeUnit.SECONDS)
+    }
+
+    @Test
+    fun testLoadCustomAnimation() {
+        testLoadCustomAnimation(OPEN_RES_ID, CLOSE_RES_ID, 0)
+    }
+
+    @Test
+    fun testLoadCustomAnimationNoEnter() {
+        testLoadCustomAnimation(0, CLOSE_RES_ID, 0)
+    }
+
+    @Test
+    fun testLoadWindowAnimations() {
+        testLoadCustomAnimation(0, 0, 30)
+    }
+
+    @Test
+    fun testCustomAnimationHigherThanWindowAnimations() {
+        testLoadCustomAnimation(OPEN_RES_ID, CLOSE_RES_ID, 30)
+    }
+
+    private fun testLoadCustomAnimation(enterResId: Int, exitResId: Int, windowAnimations: Int) {
+        val builder =
+            BackNavigationInfo.Builder()
+                .setCustomAnimation(PACKAGE_NAME, enterResId, exitResId, Color.GREEN)
+                .setWindowAnimations(PACKAGE_NAME, windowAnimations)
+        val info = builder.build().customAnimationInfo!!
+        whenever(
+                transitionAnimation.loadAnimationAttr(
+                    eq(PACKAGE_NAME),
+                    eq(windowAnimations),
+                    anyInt(),
+                    anyBoolean()
+                )
+            )
+            .thenReturn(mockCloseAnimation)
+        whenever(transitionAnimation.loadDefaultAnimationAttr(anyInt(), anyBoolean()))
+            .thenReturn(mockOpenAnimation)
+        val result = customAnimationLoader.loadAll(info)!!
+        if (exitResId != 0) {
+            if (enterResId == 0) {
+                verify(transitionAnimation, never())
+                    .loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(enterResId))
+                verify(transitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean())
+            } else {
+                assertEquals(result.enterAnimation, mockOpenAnimation)
+            }
+            assertEquals(result.backgroundColor.toLong(), Color.GREEN.toLong())
+            assertEquals(result.closeAnimation, mockCloseAnimation)
+            verify(transitionAnimation, never())
+                .loadAnimationAttr(eq(PACKAGE_NAME), anyInt(), anyInt(), anyBoolean())
+        } else if (windowAnimations != 0) {
+            verify(transitionAnimation, times(2))
+                .loadAnimationAttr(eq(PACKAGE_NAME), anyInt(), anyInt(), anyBoolean())
+            Assert.assertEquals(result.closeAnimation, mockCloseAnimation)
+        }
+    }
+
+    private fun startCustomAnimation(
+        targets: Array<RemoteAnimationTarget> =
+            arrayOf(createAnimationTarget(false), createAnimationTarget(true))
+    ): CountDownLatch {
+        val backNavigationInfo =
+            BackNavigationInfo.Builder()
+                .setCustomAnimation(PACKAGE_NAME, OPEN_RES_ID, CLOSE_RES_ID, /*backgroundColor*/ 0)
+                .build()
+        customCrossActivityBackAnimation.prepareNextAnimation(
+            backNavigationInfo.customAnimationInfo,
+            0
+        )
+        val finishCalled = CountDownLatch(1)
+        val finishCallback = Runnable { finishCalled.countDown() }
+        customCrossActivityBackAnimation
+            .getRunner()
+            .startAnimation(targets, null, null, finishCallback)
+        customCrossActivityBackAnimation.runner.callback.onBackStarted(backMotionEventFrom(0f, 0f))
+        if (targets.isNotEmpty()) {
+            verify(mockCloseAnimation)
+                .initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE))
+            verify(mockOpenAnimation)
+                .initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE))
+        }
+        return finishCalled
+    }
+
+    private fun backMotionEventFrom(touchX: Float, progress: Float) =
+        BackMotionEvent(
+            /* touchX = */ touchX,
+            /* touchY = */ 0f,
+            /* progress = */ progress,
+            /* velocityX = */ 0f,
+            /* velocityY = */ 0f,
+            /* triggerBack = */ false,
+            /* swipeEdge = */ BackEvent.EDGE_LEFT,
+            /* departingAnimationTarget = */ null
+        )
+
+    private fun createAnimationTarget(open: Boolean): RemoteAnimationTarget {
+        val topWindowLeash = SurfaceControl()
+        val taskInfo = RunningTaskInfo()
+        taskInfo.appCompatTaskInfo = appCompatTaskInfo
+        taskInfo.taskDescription = ActivityManager.TaskDescription()
+        return RemoteAnimationTarget(
+            1,
+            if (open) RemoteAnimationTarget.MODE_OPENING else RemoteAnimationTarget.MODE_CLOSING,
+            topWindowLeash,
+            false,
+            Rect(),
+            Rect(),
+            -1,
+            Point(0, 0),
+            Rect(0, 0, BOUND_SIZE, BOUND_SIZE),
+            Rect(),
+            WindowConfiguration(),
+            true,
+            null,
+            null,
+            taskInfo,
+            false,
+            -1
+        )
+    }
+
+    companion object {
+        private const val BOUND_SIZE = 100
+        private const val OPEN_RES_ID = 1000
+        private const val CLOSE_RES_ID = 1001
+        private const val PACKAGE_NAME = "TestPackage"
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
deleted file mode 100644
index 158d640..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.back;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.app.WindowConfiguration;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.Choreographer;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.animation.Animation;
-import android.window.BackNavigationInfo;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@SmallTest
-@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
-public class CustomizeActivityAnimationTest extends ShellTestCase {
-    private static final int BOUND_SIZE = 100;
-    @Mock
-    private BackAnimationBackground mBackAnimationBackground;
-    @Mock
-    private Animation mMockCloseAnimation;
-    @Mock
-    private Animation mMockOpenAnimation;
-
-    private CustomizeActivityAnimation mCustomizeActivityAnimation;
-
-    @Before
-    public void setUp() throws Exception {
-        mCustomizeActivityAnimation = new CustomizeActivityAnimation(mContext,
-                mBackAnimationBackground, mock(SurfaceControl.Transaction.class),
-                mock(Choreographer.class));
-        spyOn(mCustomizeActivityAnimation);
-        spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation);
-    }
-
-    RemoteAnimationTarget createAnimationTarget(boolean open) {
-        SurfaceControl topWindowLeash = new SurfaceControl();
-        return new RemoteAnimationTarget(1,
-                open ? RemoteAnimationTarget.MODE_OPENING : RemoteAnimationTarget.MODE_CLOSING,
-                topWindowLeash, false, new Rect(), new Rect(), -1,
-                new Point(0, 0), new Rect(0, 0, BOUND_SIZE, BOUND_SIZE), new Rect(),
-                new WindowConfiguration(), true, null, null, null, false, -1);
-    }
-
-    @Test
-    public void receiveFinishAfterInvoke() throws InterruptedException {
-        spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
-        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
-                .loadAnimation(any(), eq(false));
-        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
-                .loadAnimation(any(), eq(true));
-
-        mCustomizeActivityAnimation.prepareNextAnimation(
-                new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
-        final RemoteAnimationTarget close = createAnimationTarget(false);
-        final RemoteAnimationTarget open = createAnimationTarget(true);
-        // start animation with remote animation targets
-        final CountDownLatch finishCalled = new CountDownLatch(1);
-        final Runnable finishCallback = finishCalled::countDown;
-        mCustomizeActivityAnimation
-                .getRunner()
-                .startAnimation(
-                        new RemoteAnimationTarget[] {close, open}, null, null, finishCallback);
-        verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
-                eq(BOUND_SIZE), eq(BOUND_SIZE));
-        verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
-                eq(BOUND_SIZE), eq(BOUND_SIZE));
-
-        try {
-            mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked();
-        } catch (RemoteException r) {
-            fail("onBackInvoked throw remote exception");
-        }
-        verify(mCustomizeActivityAnimation).onGestureCommitted();
-        finishCalled.await(1, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void receiveFinishAfterCancel() throws InterruptedException {
-        spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
-        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
-                .loadAnimation(any(), eq(false));
-        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
-                .loadAnimation(any(), eq(true));
-
-        mCustomizeActivityAnimation.prepareNextAnimation(
-                new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
-        final RemoteAnimationTarget close = createAnimationTarget(false);
-        final RemoteAnimationTarget open = createAnimationTarget(true);
-        // start animation with remote animation targets
-        final CountDownLatch finishCalled = new CountDownLatch(1);
-        final Runnable finishCallback = finishCalled::countDown;
-        mCustomizeActivityAnimation
-                .getRunner()
-                .startAnimation(
-                        new RemoteAnimationTarget[] {close, open}, null, null, finishCallback);
-        verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
-                eq(BOUND_SIZE), eq(BOUND_SIZE));
-        verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
-                eq(BOUND_SIZE), eq(BOUND_SIZE));
-
-        try {
-            mCustomizeActivityAnimation.getRunner().getCallback().onBackCancelled();
-        } catch (RemoteException r) {
-            fail("onBackCancelled throw remote exception");
-        }
-        finishCalled.await(1, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void receiveFinishWithoutAnimationAfterInvoke() throws InterruptedException {
-        mCustomizeActivityAnimation.prepareNextAnimation(
-                new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
-        // start animation without any remote animation targets
-        final CountDownLatch finishCalled = new CountDownLatch(1);
-        final Runnable finishCallback = finishCalled::countDown;
-        mCustomizeActivityAnimation
-                .getRunner()
-                .startAnimation(new RemoteAnimationTarget[] {}, null, null, finishCallback);
-
-        try {
-            mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked();
-        } catch (RemoteException r) {
-            fail("onBackInvoked throw remote exception");
-        }
-        verify(mCustomizeActivityAnimation).onGestureCommitted();
-        finishCalled.await(1, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testLoadCustomAnimation() {
-        testLoadCustomAnimation(10, 20, 0);
-    }
-
-    @Test
-    public void testLoadCustomAnimationNoEnter() {
-        testLoadCustomAnimation(0, 10, 0);
-    }
-
-    @Test
-    public void testLoadWindowAnimations() {
-        testLoadCustomAnimation(0, 0, 30);
-    }
-
-    @Test
-    public void testCustomAnimationHigherThanWindowAnimations() {
-        testLoadCustomAnimation(10, 20, 30);
-    }
-
-    private void testLoadCustomAnimation(int enterResId, int exitResId, int windowAnimations) {
-        final String testPackage = "TestPackage";
-        BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder()
-                .setCustomAnimation(testPackage, enterResId, exitResId, Color.GREEN)
-                .setWindowAnimations(testPackage, windowAnimations);
-        final BackNavigationInfo.CustomAnimationInfo info = builder.build()
-                .getCustomAnimationInfo();
-
-        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
-                        .mTransitionAnimation)
-                .loadAppTransitionAnimation(eq(testPackage), eq(enterResId));
-        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
-                        .mTransitionAnimation)
-                .loadAppTransitionAnimation(eq(testPackage), eq(exitResId));
-        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
-                        .mTransitionAnimation)
-                .loadAnimationAttr(eq(testPackage), eq(windowAnimations), anyInt(), anyBoolean());
-        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
-                        .mTransitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean());
-
-        CustomizeActivityAnimation.AnimationLoadResult result =
-                mCustomizeActivityAnimation.mCustomAnimationLoader.loadAll(info);
-
-        if (exitResId != 0) {
-            if (enterResId == 0) {
-                verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation,
-                        never()).loadAppTransitionAnimation(eq(testPackage), eq(enterResId));
-                verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation)
-                        .loadDefaultAnimationAttr(anyInt(), anyBoolean());
-            } else {
-                assertEquals(result.mEnterAnimation, mMockOpenAnimation);
-            }
-            assertEquals(result.mBackgroundColor, Color.GREEN);
-            assertEquals(result.mCloseAnimation, mMockCloseAnimation);
-            verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, never())
-                    .loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean());
-        } else if (windowAnimations != 0) {
-            verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation,
-                    times(2)).loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean());
-            assertEquals(result.mCloseAnimation, mMockCloseAnimation);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index afae653..9c00864 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -668,6 +668,18 @@
         Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
     }
 
+    @Test
+    public void testLetterboxEduLayout_notCreatedWhenLetterboxEducationIsDisabled() {
+        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
+                CAMERA_COMPAT_CONTROL_HIDDEN);
+        taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled = false;
+
+        mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
+
+        verify(mController, never()).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
+    }
+
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
             @CameraCompatControlState int cameraCompatControlState) {
         return createTaskInfo(displayId, taskId, hasSizeCompat, cameraCompatControlState,
@@ -694,6 +706,8 @@
         taskInfo.isVisible = isVisible;
         taskInfo.isFocused = isFocused;
         taskInfo.isTopActivityTransparent = isTopActivityTransparent;
+        taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled = true;
+        taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed = true;
         return taskInfo;
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 60a7dcd..2a2483d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -41,6 +41,7 @@
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
+import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.TransitionInfoBuilder
 import com.android.wm.shell.transition.Transitions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index dca7be1..8f59f30 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -182,18 +182,6 @@
     }
 
     @Test
-    fun addListener_notifiesStashed() {
-        repo.setStashed(DEFAULT_DISPLAY, true)
-        val listener = TestVisibilityListener()
-        val executor = TestShellExecutor()
-        repo.addVisibleTasksListener(listener, executor)
-        executor.flushAll()
-
-        assertThat(listener.stashedOnDefaultDisplay).isTrue()
-        assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1)
-    }
-
-    @Test
     fun addListener_tasksOnDifferentDisplay_doesNotNotify() {
         repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true)
         val listener = TestVisibilityListener()
@@ -400,65 +388,6 @@
     }
 
     @Test
-    fun setStashed_stateIsUpdatedForTheDisplay() {
-        repo.setStashed(DEFAULT_DISPLAY, true)
-        assertThat(repo.isStashed(DEFAULT_DISPLAY)).isTrue()
-        assertThat(repo.isStashed(SECOND_DISPLAY)).isFalse()
-
-        repo.setStashed(DEFAULT_DISPLAY, false)
-        assertThat(repo.isStashed(DEFAULT_DISPLAY)).isFalse()
-    }
-
-    @Test
-    fun setStashed_notifyListener() {
-        val listener = TestVisibilityListener()
-        val executor = TestShellExecutor()
-        repo.addVisibleTasksListener(listener, executor)
-        repo.setStashed(DEFAULT_DISPLAY, true)
-        executor.flushAll()
-        assertThat(listener.stashedOnDefaultDisplay).isTrue()
-        assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1)
-
-        repo.setStashed(DEFAULT_DISPLAY, false)
-        executor.flushAll()
-        assertThat(listener.stashedOnDefaultDisplay).isFalse()
-        assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(2)
-    }
-
-    @Test
-    fun setStashed_secondCallDoesNotNotify() {
-        val listener = TestVisibilityListener()
-        val executor = TestShellExecutor()
-        repo.addVisibleTasksListener(listener, executor)
-        repo.setStashed(DEFAULT_DISPLAY, true)
-        repo.setStashed(DEFAULT_DISPLAY, true)
-        executor.flushAll()
-        assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1)
-    }
-
-    @Test
-    fun setStashed_tracksPerDisplay() {
-        val listener = TestVisibilityListener()
-        val executor = TestShellExecutor()
-        repo.addVisibleTasksListener(listener, executor)
-
-        repo.setStashed(DEFAULT_DISPLAY, true)
-        executor.flushAll()
-        assertThat(listener.stashedOnDefaultDisplay).isTrue()
-        assertThat(listener.stashedOnSecondaryDisplay).isFalse()
-
-        repo.setStashed(SECOND_DISPLAY, true)
-        executor.flushAll()
-        assertThat(listener.stashedOnDefaultDisplay).isTrue()
-        assertThat(listener.stashedOnSecondaryDisplay).isTrue()
-
-        repo.setStashed(DEFAULT_DISPLAY, false)
-        executor.flushAll()
-        assertThat(listener.stashedOnDefaultDisplay).isFalse()
-        assertThat(listener.stashedOnSecondaryDisplay).isTrue()
-    }
-
-    @Test
     fun removeFreeformTask_removesTaskBoundsBeforeMaximize() {
         val taskId = 1
         repo.saveBoundsBeforeMaximize(taskId, Rect(0, 0, 200, 200))
@@ -598,12 +527,6 @@
         var visibleChangesOnDefaultDisplay = 0
         var visibleChangesOnSecondaryDisplay = 0
 
-        var stashedOnDefaultDisplay = false
-        var stashedOnSecondaryDisplay = false
-
-        var stashedChangesOnDefaultDisplay = 0
-        var stashedChangesOnSecondaryDisplay = 0
-
         override fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {
             when (displayId) {
                 DEFAULT_DISPLAY -> {
@@ -617,20 +540,6 @@
                 else -> fail("Visible task listener received unexpected display id: $displayId")
             }
         }
-
-        override fun onStashedChanged(displayId: Int, stashed: Boolean) {
-            when (displayId) {
-                DEFAULT_DISPLAY -> {
-                    stashedOnDefaultDisplay = stashed
-                    stashedChangesOnDefaultDisplay++
-                }
-                SECOND_DISPLAY -> {
-                    stashedOnSecondaryDisplay = stashed
-                    stashedChangesOnDefaultDisplay++
-                }
-                else -> fail("Visible task listener received unexpected display id: $displayId")
-            }
-        }
     }
 
     companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
new file mode 100644
index 0000000..285e5b6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.Companion.DesktopUiEventEnum.DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test class for [DesktopModeUiEventLogger]
+ *
+ * Usage: atest WMShellUnitTests:DesktopModeUiEventLoggerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopModeUiEventLoggerTest : ShellTestCase() {
+    private lateinit var uiEventLoggerFake: UiEventLoggerFake
+    private lateinit var logger: DesktopModeUiEventLogger
+    private val instanceIdSequence = InstanceIdSequence(10)
+
+
+    @Before
+    fun setUp() {
+        uiEventLoggerFake = UiEventLoggerFake()
+        logger = DesktopModeUiEventLogger(uiEventLoggerFake, instanceIdSequence)
+    }
+
+    @Test
+    fun log_invalidUid_eventNotLogged() {
+        logger.log(-1, PACKAGE_NAME, DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+    }
+
+    @Test
+    fun log_emptyPackageName_eventNotLogged() {
+        logger.log(UID, "", DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+    }
+
+    @Test
+    fun log_eventLogged() {
+        val event =
+            DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+        logger.log(UID, PACKAGE_NAME, event)
+        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+        assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id)
+        assertThat(uiEventLoggerFake[0].instanceId).isNull()
+        assertThat(uiEventLoggerFake[0].uid).isEqualTo(UID)
+        assertThat(uiEventLoggerFake[0].packageName).isEqualTo(PACKAGE_NAME)
+    }
+
+    @Test
+    fun getNewInstanceId() {
+        val first = logger.getNewInstanceId()
+        assertThat(first).isNotEqualTo(logger.getNewInstanceId())
+    }
+
+    @Test
+    fun logWithInstanceId_invalidUid_eventNotLogged() {
+        logger.logWithInstanceId(INSTANCE_ID, -1, PACKAGE_NAME, DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+    }
+
+    @Test
+    fun logWithInstanceId_emptyPackageName_eventNotLogged() {
+        logger.logWithInstanceId(INSTANCE_ID, UID, "", DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+    }
+
+    @Test
+    fun logWithInstanceId_eventLogged() {
+        val event =
+            DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+        logger.logWithInstanceId(INSTANCE_ID, UID, PACKAGE_NAME, event)
+        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+        assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id)
+        assertThat(uiEventLoggerFake[0].instanceId).isEqualTo(INSTANCE_ID)
+        assertThat(uiEventLoggerFake[0].uid).isEqualTo(UID)
+        assertThat(uiEventLoggerFake[0].packageName).isEqualTo(PACKAGE_NAME)
+    }
+
+
+    companion object {
+        private val INSTANCE_ID = InstanceId.fakeInstanceId(0)
+        private const val UID = 10
+        private const val PACKAGE_NAME = "com.foo"
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 3f76c4f..f67da55 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -79,6 +79,7 @@
 import com.android.wm.shell.draganddrop.DragAndDropController
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
@@ -1044,29 +1045,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-    fun handleRequest_fullscreenTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
-        assumeTrue(ENABLE_SHELL_TRANSITIONS)
-        whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
-
-        val stashedFreeformTask = setUpFreeformTask(DEFAULT_DISPLAY)
-        markTaskHidden(stashedFreeformTask)
-
-        val fullscreenTask = createFullscreenTask(DEFAULT_DISPLAY)
-
-        controller.stashDesktopApps(DEFAULT_DISPLAY)
-
-        val result = controller.handleRequest(Binder(), createTransition(fullscreenTask))
-        assertThat(result).isNotNull()
-        result!!.assertReorderSequence(stashedFreeformTask, fullscreenTask)
-        assertThat(result.changes[fullscreenTask.token.asBinder()]?.windowingMode)
-                .isEqualTo(WINDOWING_MODE_FREEFORM)
-
-        // Stashed state should be cleared
-        assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse()
-    }
-
-    @Test
     fun handleRequest_freeformTask_freeformVisible_returnNull() {
         assumeTrue(ENABLE_SHELL_TRANSITIONS)
 
@@ -1133,27 +1111,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-    fun handleRequest_freeformTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
-        assumeTrue(ENABLE_SHELL_TRANSITIONS)
-        whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
-
-        val stashedFreeformTask = setUpFreeformTask(DEFAULT_DISPLAY)
-        markTaskHidden(stashedFreeformTask)
-
-        val freeformTask = createFreeformTask(DEFAULT_DISPLAY)
-
-        controller.stashDesktopApps(DEFAULT_DISPLAY)
-
-        val result = controller.handleRequest(Binder(), createTransition(freeformTask))
-        assertThat(result).isNotNull()
-        result?.assertReorderSequence(stashedFreeformTask, freeformTask)
-
-        // Stashed state should be cleared
-        assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse()
-    }
-
-    @Test
     fun handleRequest_notOpenOrToFrontTransition_returnNull() {
         assumeTrue(ENABLE_SHELL_TRANSITIONS)
 
@@ -1269,29 +1226,6 @@
     }
 
     @Test
-    fun stashDesktopApps_stateUpdates() {
-        whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
-
-        controller.stashDesktopApps(DEFAULT_DISPLAY)
-
-        assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isTrue()
-        assertThat(desktopModeTaskRepository.isStashed(SECOND_DISPLAY)).isFalse()
-    }
-
-    @Test
-    fun hideStashedDesktopApps_stateUpdates() {
-        whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
-
-        desktopModeTaskRepository.setStashed(DEFAULT_DISPLAY, true)
-        desktopModeTaskRepository.setStashed(SECOND_DISPLAY, true)
-        controller.hideStashedDesktopApps(DEFAULT_DISPLAY)
-
-        assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse()
-        // Check that second display is not affected
-        assertThat(desktopModeTaskRepository.isStashed(SECOND_DISPLAY)).isTrue()
-    }
-
-    @Test
     fun desktopTasksVisibilityChange_visible_setLaunchAdjacentDisabled() {
         val task = setUpFreeformTask()
         clearInvocations(launchAdjacentController)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 539d5b8..3c488ca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -32,6 +32,7 @@
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.transition.TransitionInfoBuilder
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.util.StubTransaction
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 665077b..cd68c69 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -35,8 +35,8 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 240324b..884cb6e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -67,8 +67,8 @@
 import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 7d19f3c..aa2cee7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -60,8 +60,8 @@
 import com.android.wm.shell.common.DisplayLayout
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
-import com.android.wm.shell.desktopmode.DesktopModeStatus
 import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.sysui.KeyguardChangeListener
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 4eb44d7..8b8cd11 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -75,7 +75,7 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
 import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.tests.R;
 
 import org.junit.Before;
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
index b511244..6196589 100644
--- a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_base_libs_androidfw_license"],
+    default_team: "trendy_team_android_resources",
 }
 
 cc_fuzz {
@@ -31,7 +32,7 @@
     static_libs: ["libgmock"],
     target: {
         android: {
-            shared_libs:[
+            shared_libs: [
                 "libandroidfw",
                 "libbase",
                 "libcutils",
@@ -52,4 +53,15 @@
             ],
         },
     },
+    fuzz_config: {
+        cc: [
+            "android-resources@google.com",
+        ],
+        componentid: 568761,
+        description: "The fuzzer targets the APIs of libandroidfw",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
+    },
 }
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 753a699..7c1c5b4 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -336,6 +336,7 @@
         "jni/android_graphics_animation_NativeInterpolatorFactory.cpp",
         "jni/android_graphics_animation_RenderNodeAnimator.cpp",
         "jni/android_graphics_Canvas.cpp",
+        "jni/android_graphics_Color.cpp",
         "jni/android_graphics_ColorSpace.cpp",
         "jni/android_graphics_drawable_AnimatedVectorDrawable.cpp",
         "jni/android_graphics_drawable_VectorDrawable.cpp",
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index fd9915a..70a9ef0 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -46,6 +46,7 @@
 
 extern int register_android_graphics_Canvas(JNIEnv* env);
 extern int register_android_graphics_CanvasProperty(JNIEnv* env);
+extern int register_android_graphics_Color(JNIEnv* env);
 extern int register_android_graphics_ColorFilter(JNIEnv* env);
 extern int register_android_graphics_ColorSpace(JNIEnv* env);
 extern int register_android_graphics_DrawFilter(JNIEnv* env);
@@ -87,6 +88,7 @@
         {"android.graphics.Camera", REG_JNI(register_android_graphics_Camera)},
         {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)},
         {"android.graphics.CanvasProperty", REG_JNI(register_android_graphics_CanvasProperty)},
+        {"android.graphics.Color", REG_JNI(register_android_graphics_Color)},
         {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)},
         {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)},
         {"android.graphics.CreateJavaOutputStreamAdaptor",
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index fb0cdb0..6ace396 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -49,6 +49,7 @@
 extern int register_android_graphics_Canvas(JNIEnv* env);
 extern int register_android_graphics_CanvasProperty(JNIEnv* env);
 extern int register_android_graphics_ColorFilter(JNIEnv* env);
+extern int register_android_graphics_Color(JNIEnv* env);
 extern int register_android_graphics_ColorSpace(JNIEnv* env);
 extern int register_android_graphics_DrawFilter(JNIEnv* env);
 extern int register_android_graphics_FontFamily(JNIEnv* env);
@@ -98,6 +99,7 @@
 
     static const RegJNIRec gRegJNI[] = {
             REG_JNI(register_android_graphics_Canvas),
+            REG_JNI(register_android_graphics_Color),
             // This needs to be before register_android_graphics_Graphics, or the latter
             // will not be able to find the jmethodID for ColorSpace.get().
             REG_JNI(register_android_graphics_ColorSpace),
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index a952be0..2a057e7 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -36,25 +36,6 @@
         return 0;                   \
     }
 
-static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
-{
-    SkScalar hsv[3];
-    SkRGBToHSV(red, green, blue, hsv);
-
-    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
-    float* values = autoHSV.ptr();
-    for (int i = 0; i < 3; i++) {
-        values[i] = SkScalarToFloat(hsv[i]);
-    }
-}
-
-static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
-{
-    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
-    SkScalar* hsv = autoHSV.ptr();
-    return static_cast<jint>(SkHSVToColor(alpha, hsv));
-}
-
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 static void Shader_safeUnref(SkShader* shader) {
@@ -409,11 +390,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static const JNINativeMethod gColorMethods[] = {
-    { "nativeRGBToHSV",    "(III[F)V", (void*)Color_RGBToHSV   },
-    { "nativeHSVToColor",  "(I[F)I",   (void*)Color_HSVToColor }
-};
-
 static const JNINativeMethod gShaderMethods[] = {
     { "nativeGetFinalizer",   "()J",    (void*)Shader_getNativeFinalizer },
 };
@@ -456,8 +432,6 @@
 
 int register_android_graphics_Shader(JNIEnv* env)
 {
-    android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
-                                  NELEM(gColorMethods));
     android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
                                   NELEM(gShaderMethods));
     android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
diff --git a/libs/hwui/jni/android_graphics_Color.cpp b/libs/hwui/jni/android_graphics_Color.cpp
new file mode 100644
index 0000000..c22b8b9
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_Color.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GraphicsJNI.h"
+
+#include "SkColor.h"
+
+using namespace android;
+
+static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue,
+                           jfloatArray hsvArray)
+{
+    SkScalar hsv[3];
+    SkRGBToHSV(red, green, blue, hsv);
+
+    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
+    float* values = autoHSV.ptr();
+    for (int i = 0; i < 3; i++) {
+        values[i] = SkScalarToFloat(hsv[i]);
+    }
+}
+
+static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
+{
+    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
+    SkScalar* hsv = autoHSV.ptr();
+    return static_cast<jint>(SkHSVToColor(alpha, hsv));
+}
+
+static const JNINativeMethod gColorMethods[] = {
+    { "nativeRGBToHSV",    "(III[F)V", (void*)Color_RGBToHSV   },
+    { "nativeHSVToColor",  "(I[F)I",   (void*)Color_HSVToColor }
+};
+
+namespace android {
+
+int register_android_graphics_Color(JNIEnv* env) {
+    return android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
+                                         NELEM(gColorMethods));
+}
+
+}; // namespace android
diff --git a/libs/hwui/jni/android_graphics_ColorSpace.cpp b/libs/hwui/jni/android_graphics_ColorSpace.cpp
index 63d3f83..d06206b 100644
--- a/libs/hwui/jni/android_graphics_ColorSpace.cpp
+++ b/libs/hwui/jni/android_graphics_ColorSpace.cpp
@@ -148,7 +148,7 @@
 namespace android {
 
 int register_android_graphics_ColorSpace(JNIEnv* env) {
-    return android::RegisterMethodsOrDie(env, "android/graphics/ColorSpace$Rgb",
+    return android::RegisterMethodsOrDie(env, "android/graphics/ColorSpace$Rgb$Native",
                                          gColorSpaceRgbMethods, NELEM(gColorSpaceRgbMethods));
 }
 
diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp
index c0d791a..eedc069 100644
--- a/libs/hwui/jni/android_graphics_Matrix.cpp
+++ b/libs/hwui/jni/android_graphics_Matrix.cpp
@@ -326,9 +326,6 @@
 };
 
 static const JNINativeMethod methods[] = {
-    {"nGetNativeFinalizer", "()J", (void*) SkMatrixGlue::getNativeFinalizer},
-    {"nCreate","(J)J", (void*) SkMatrixGlue::create},
-
     // ------- @FastNative below here ---------------
     {"nMapPoints","(J[FI[FIIZ)V", (void*) SkMatrixGlue::mapPoints},
     {"nMapRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;)Z",
@@ -388,9 +385,6 @@
 int register_android_graphics_Matrix(JNIEnv* env) {
     // Methods only used on Ravenwood (for now). See the javadoc on Matrix$ExtraNativesx
     // for why we need it.
-    //
-    // We don't need it on non-ravenwood, but we don't (yet) have a way to detect ravenwood
-    // environment, so we just always run it.
     RegisterMethodsOrDie(env, "android/graphics/Matrix$ExtraNatives", extra_methods,
                          NELEM(extra_methods));
 
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 5cf5a1d..f1ee325 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -467,10 +467,10 @@
 
     std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1);
     /*
-     * Using ui::ADISPLAY_ID_NONE for displayId here to avoid removing the callback
+     * Using ui::LogicalDisplayId::INVALID for displayId here to avoid removing the callback
      * if a TouchSpotController with the same display is removed.
      */
-    mContext.addAnimationCallback(ui::ADISPLAY_ID_NONE, func);
+    mContext.addAnimationCallback(ui::LogicalDisplayId::INVALID, func);
 }
 
 } // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 70e5c24..c6430f7 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -109,7 +109,7 @@
 
     struct Locked {
         Presentation presentation;
-        ui::LogicalDisplayId pointerDisplayId = ui::ADISPLAY_ID_NONE;
+        ui::LogicalDisplayId pointerDisplayId = ui::LogicalDisplayId::INVALID;
 
         std::vector<gui::DisplayInfo> mDisplayInfos;
         std::unordered_map<ui::LogicalDisplayId, TouchSpotController> spotControllers;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 070c90c..e147c56 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -174,7 +174,7 @@
         int32_t layer{0};
         float alpha{1.0f};
         SpriteTransformationMatrix transformationMatrix;
-        ui::LogicalDisplayId displayId{ui::ADISPLAY_ID_DEFAULT};
+        ui::LogicalDisplayId displayId{ui::LogicalDisplayId::DEFAULT};
 
         sp<SurfaceControl> surfaceControl;
         int32_t surfaceWidth{0};
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 7a13380..2dcb1f1 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -166,7 +166,7 @@
     PointerControllerTest();
     ~PointerControllerTest();
 
-    void ensureDisplayViewportIsSet(ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_DEFAULT);
+    void ensureDisplayViewportIsSet(ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT);
 
     sp<MockSprite> mPointerSprite;
     sp<MockPointerControllerPolicyInterface> mPolicy;
@@ -335,23 +335,23 @@
 
     // Update spots to sync state with sprite
     mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
-                                 ui::ADISPLAY_ID_DEFAULT);
+                                 ui::LogicalDisplayId::DEFAULT);
     testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
 
     // Marking the display to skip screenshot should update sprite as well
-    mPointerController->setSkipScreenshot(ui::ADISPLAY_ID_DEFAULT, true);
+    mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, true);
     EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(true));
 
     // Update spots to sync state with sprite
     mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
-                                 ui::ADISPLAY_ID_DEFAULT);
+                                 ui::LogicalDisplayId::DEFAULT);
     testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
 
     // Reset flag and verify again
-    mPointerController->setSkipScreenshot(ui::ADISPLAY_ID_DEFAULT, false);
+    mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, false);
     EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(false));
     mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
-                                 ui::ADISPLAY_ID_DEFAULT);
+                                 ui::LogicalDisplayId::DEFAULT);
     testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
 }
 
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index b43ff63..a488756 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -252,18 +252,14 @@
         return 0;
     }
 
-    /**
-     * Get the current playback info for this session.
-     *
-     * @return The current playback info or null.
-     */
-    public @Nullable PlaybackInfo getPlaybackInfo() {
+    /** Returns the current playback info for this session. */
+    @NonNull
+    public PlaybackInfo getPlaybackInfo() {
         try {
             return mSessionBinder.getVolumeAttributes();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getAudioInfo.", e);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
-        return null;
     }
 
     /**
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index 6d4cc3a..cf7aea4 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -64,8 +64,10 @@
   }
 
   public final class NfcAdapter {
+    method @FlaggedApi("android.nfc.nfc_state_change") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable();
     method public void disableForegroundDispatch(android.app.Activity);
     method public void disableReaderMode(android.app.Activity);
+    method @FlaggedApi("android.nfc.nfc_state_change") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
     method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]);
     method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
     method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 310130e..a33e225 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -3,9 +3,7 @@
 
   public final class NfcAdapter {
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean addNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler, String[]);
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean);
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
     method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getAdapterState();
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index e43d104..06098de 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -1106,6 +1106,9 @@
      * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the
      * operation is complete.
      *
+     * <p>This API is only allowed to be called by system apps
+     * or apps which are Device Owner or Profile Owner.
+     *
      * <p>If this returns true, then either NFC is already on, or
      * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent
      * to indicate a state transition. If this returns false, then
@@ -1113,9 +1116,8 @@
      * NFC on (for example we are in airplane mode and NFC is not
      * toggleable in airplane mode on this platform).
      *
-     * @hide
      */
-    @SystemApi
+    @FlaggedApi(Flags.FLAG_NFC_STATE_CHANGE)
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean enable() {
         try {
@@ -1146,15 +1148,17 @@
      * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the
      * operation is complete.
      *
+     * <p>This API is only allowed to be called by system apps
+     * or apps which are Device Owner or Profile Owner.
+     *
      * <p>If this returns true, then either NFC is already off, or
      * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent
      * to indicate a state transition. If this returns false, then
      * there is some problem that prevents an attempt to turn
      * NFC off.
      *
-     * @hide
      */
-    @SystemApi
+    @FlaggedApi(Flags.FLAG_NFC_STATE_CHANGE)
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean disable() {
         try {
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index f674b06a..c3c74a6 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.app.Service;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -404,6 +405,7 @@
      *
      * @param frame A description of the polling frame.
      */
+    @SuppressLint("OnNameExpected")
     @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
     public void processPollingFrames(@NonNull List<PollingFrame> frame) {
     }
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 73b29db..cb2a48c 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -93,3 +93,11 @@
     description: "Enable NFC OEM extension support"
     bug: "331206243"
 }
+
+flag {
+    name: "nfc_state_change"
+    is_exported: true
+    namespace: "nfc"
+    description: "Enable nfc state change API"
+    bug: "319934052"
+}
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index a33e0dc2..7015c50 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -38,7 +38,7 @@
     <string name="helper_title_computer" msgid="4671071173916176037">"Services Google Play"</string>
     <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> pour accéder aux photos, contenus multimédias et notifications de votre téléphone"</string>
     <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Autoriser &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; à effectuer cette action ?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Autoriser &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; à caster les applications et les fonctionnalités système de votre téléphone ?"</string>
+    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Autoriser &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; à streamer les applications et les fonctionnalités système de votre téléphone ?"</string>
     <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s aura accès à tout ce qui est visible ou lu sur le téléphone, y compris les contenus audio, les photos, les informations de paiement, les mots de passe et les messages.&lt;br/&gt;&lt;br/&gt;%1$s pourra caster des applications et des fonctionnalités système jusqu\'à ce que vous supprimiez l\'accès à cette autorisation."</string>
     <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de diffuser des applis et d\'autres fonctionnalités système en streaming sur des appareils à proximité"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index 4985ae3c..c929ee4 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -54,7 +54,7 @@
     <string name="vendor_header_button_description" msgid="7994879208461111473">"További információ"</string>
     <string name="permission_phone" msgid="2661081078692784919">"Telefon"</string>
     <string name="permission_sms" msgid="6337141296535774786">"SMS"</string>
-    <string name="permission_contacts" msgid="3858319347208004438">"Címtár"</string>
+    <string name="permission_contacts" msgid="3858319347208004438">"Névjegyek"</string>
     <string name="permission_calendar" msgid="6805668388691290395">"Naptár"</string>
     <string name="permission_microphone" msgid="2152206421428732949">"Mikrofon"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"Hívásnaplók"</string>
diff --git a/packages/CredentialManager/res/values-es-rUS/strings.xml b/packages/CredentialManager/res/values-es-rUS/strings.xml
index ef27359..f749909 100644
--- a/packages/CredentialManager/res/values-es-rUS/strings.xml
+++ b/packages/CredentialManager/res/values-es-rUS/strings.xml
@@ -91,7 +91,7 @@
     <string name="no_sign_in_info_in" msgid="2641118151920288356">"No hay información de acceso en <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Administrar accesos"</string>
     <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Desde otro dispositivo"</string>
-    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar otra voz"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar otro dispositivo"</string>
     <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> canceló la solicitud"</string>
     <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"Opciones de acceso"</string>
     <string name="more_options_content_description" msgid="1323427365788198808">"Más"</string>
diff --git a/packages/CredentialManager/res/values-or/strings.xml b/packages/CredentialManager/res/values-or/strings.xml
index e3a9191..9885a1f 100644
--- a/packages/CredentialManager/res/values-or/strings.xml
+++ b/packages/CredentialManager/res/values-or/strings.xml
@@ -57,9 +57,9 @@
     <string name="set_as_default" msgid="4415328591568654603">"ଡିଫଲ୍ଟ ଭାବେ ସେଟ କରନ୍ତୁ"</string>
     <string name="settings" msgid="6536394145760913145">"ସେଟିଂସ"</string>
     <string name="use_once" msgid="9027366575315399714">"ଥରେ ବ୍ୟବହାର କରନ୍ତୁ"</string>
-    <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ଟି ପାସୱାର୍ଡ • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>ଟି ପାସକୀ"</string>
-    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ଟି ପାସୱାର୍ଡ"</string>
-    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>ଟି ପାସକୀ"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ପାସୱାର୍ଡ • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ପାସକୀ"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ପାସୱାର୍ଡ"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> ପାସକୀ"</string>
     <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g>ଟି କ୍ରେଡେନସିଆଲ"</string>
     <string name="passkey_before_subtitle" msgid="2448119456208647444">"ପାସକୀ"</string>
     <string name="another_device" msgid="5147276802037801217">"ଅନ୍ୟ ଏକ ଡିଭାଇସ"</string>
diff --git a/packages/CredentialManager/res/values-zh-rHK/strings.xml b/packages/CredentialManager/res/values-zh-rHK/strings.xml
index 44f5eaa..c6ac743 100644
--- a/packages/CredentialManager/res/values-zh-rHK/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rHK/strings.xml
@@ -74,7 +74,7 @@
     <string name="get_dialog_description_single_tap" msgid="2797059565126030879">"使用螢幕鎖定方式以 <xliff:g id="USERNAME">%2$s</xliff:g> 登入 <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="get_dialog_title_unlock_options_for" msgid="7096423827682163270">"解鎖 <xliff:g id="APP_NAME">%1$s</xliff:g> 的登入選項"</string>
     <string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"選擇已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」密鑰"</string>
-    <string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"選擇已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」密碼"</string>
+    <string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"選擇「<xliff:g id="APP_NAME">%1$s</xliff:g>」的儲存密碼"</string>
     <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"選擇已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資料"</string>
     <string name="get_dialog_title_choose_sign_in_for" msgid="645728947702442421">"選擇使用 <xliff:g id="APP_NAME">%1$s</xliff:g> 的帳戶"</string>
     <string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"要選擇適用於「<xliff:g id="APP_NAME">%1$s</xliff:g>」的項目嗎?"</string>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index d4a8110..7bc25ed 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -174,11 +174,8 @@
                 onUserCancel()
             } else {
                 Log.d(Constants.LOG_TAG, "The provider activity was cancelled," +
-                    " re-displaying our UI.")
-                uiState = uiState.copy(
-                    selectedEntry = null,
-                    providerActivityState = ProviderActivityState.NOT_APPLICABLE,
-                )
+                            " re-displaying our UI.")
+                resetUiStateForReLaunch()
             }
         } else {
             if (entry != null) {
@@ -202,6 +199,15 @@
         }
     }
 
+    // Resets UI states for any situation that re-launches the UI
+    private fun resetUiStateForReLaunch() {
+        onBiometricPromptStateChange(BiometricPromptState.INACTIVE)
+        uiState = uiState.copy(
+            selectedEntry = null,
+            providerActivityState = ProviderActivityState.NOT_APPLICABLE,
+        )
+    }
+
     fun onLastLockedAuthEntryNotFoundError() {
         Log.d(Constants.LOG_TAG, "Unable to find the last unlocked entry")
         onInternalError()
@@ -502,4 +508,4 @@
     fun logUiEvent(uiEventEnum: UiEventEnum) {
         this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo?.packageName)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 25ac3c9..635dc42 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -172,7 +172,7 @@
 
     // This is for testing only now
     private boolean mEnableWhenCompleted;
-    private boolean mOneShot;
+    private boolean mOneShot = true;
     private boolean mHideNotification;
 
     private InstallationAsyncTask.Progress mInstallTaskProgress;
diff --git a/packages/PrintSpooler/TEST_MAPPING b/packages/PrintSpooler/TEST_MAPPING
index 4fa8822..ad3b44f 100644
--- a/packages/PrintSpooler/TEST_MAPPING
+++ b/packages/PrintSpooler/TEST_MAPPING
@@ -8,5 +8,10 @@
         }
       ]
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "PrintSpoolerOutOfProcessTests"
+    }
   ]
 }
diff --git a/packages/PrintSpooler/res/values-night/themes.xml b/packages/PrintSpooler/res/values-night/themes.xml
index 3cc64a6..76fa7b9 100644
--- a/packages/PrintSpooler/res/values-night/themes.xml
+++ b/packages/PrintSpooler/res/values-night/themes.xml
@@ -24,6 +24,7 @@
     <style name="Theme.SelectPrinterActivity"
            parent="android:style/Theme.DeviceDefault">
         <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
     </style>
 
     <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault">
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index bd96025..22842f7 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -24,6 +24,7 @@
            parent="android:style/Theme.DeviceDefault.Light">
         <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
         <item name="android:windowLightStatusBar">true</item>
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
     </style>
 
     <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault.Light">
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index d25d5dc..ff09084 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -785,6 +785,9 @@
                 } else {
                     onPrinterUnavailable(printerInfo);
                 }
+                if (mPrinterRegistry != null) {
+                    mPrinterRegistry.setTrackedPrinter(mCurrentPrinter.getId());
+                }
 
                 mDestinationSpinnerAdapter.ensurePrinterInVisibleAdapterPosition(printerInfo);
 
diff --git a/packages/SettingsLib/DataStore/README.md b/packages/SettingsLib/DataStore/README.md
index 30cb993..a762ad3 100644
--- a/packages/SettingsLib/DataStore/README.md
+++ b/packages/SettingsLib/DataStore/README.md
@@ -1,55 +1,93 @@
 # Datastore library
 
-This library aims to manage datastore in a consistent way.
+This library provides consistent API for data management (including backup,
+restore, and metrics logging) on Android platform.
+
+Notably, it is designed to be flexible and could be utilized for a wide range of
+data store besides the settings preferences.
 
 ## Overview
 
-A datastore is required to extend the `BackupRestoreStorage` class and implement
-either `Observable` or `KeyedObservable` interface, which enforces:
+In the high-level design, a persistent datastore aims to support two key
+characteristics:
 
--   Backup and restore: Datastore should support
-    [data backup](https://developer.android.com/guide/topics/data/backup) to
-    preserve user experiences on a new device.
--   Observer pattern: The
-    [observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) allows to
-    monitor data change in the datastore and
-    -   trigger
-        [BackupManager.dataChanged](https://developer.android.com/reference/android/app/backup/BackupManager#dataChanged\(\))
-        automatically.
-    -   track data change event to log metrics.
-    -   update internal state and take action.
+-   **observable**: triggers backup and metrics logging whenever data is
+    changed.
+-   **transferable**: offers users with a seamless experience by backing up and
+    restoring data on to new devices.
+
+More specifically, Android framework supports
+[data backup](https://developer.android.com/guide/topics/data/backup) to
+preserve user experiences on a new device. And the
+[observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) allows to
+monitor data change.
 
 ### Backup and restore
 
-The Android backup framework provides
+Currently, the Android backup framework provides
 [BackupAgentHelper](https://developer.android.com/reference/android/app/backup/BackupAgentHelper)
 and
 [BackupHelper](https://developer.android.com/reference/android/app/backup/BackupHelper)
-to back up a datastore. However, there are several caveats when implement
-`BackupHelper`:
+to facilitate data backup. However, there are several caveats to consider when
+implementing `BackupHelper`:
 
--   performBackup: The data is updated incrementally but it is not well
+-   *performBackup*: The data is updated incrementally but it is not well
     documented. The `ParcelFileDescriptor` state parameters are normally ignored
     and data is updated even there is no change.
--   restoreEntity: The implementation must take care not to seek or close the
-    underlying data source, nor read more than size() bytes from the stream when
-    restore (see
+-   *restoreEntity*: The implementation must take care not to seek or close the
+    underlying data source, nor read more than `size()` bytes from the stream
+    when restore (see
     [BackupDataInputStream](https://developer.android.com/reference/android/app/backup/BackupDataInputStream)).
-    It is possible a `BackupHelper` prevents other `BackupHelper`s from
-    restoring data.
--   writeNewStateDescription: Existing implementations rarely notice that this
-    callback is invoked after all entities are restored, and check if necessary
-    data are all restored in `restoreEntity` (e.g.
+    It is possible that a `BackupHelper` interferes with the restore process of
+    other `BackupHelper`s.
+-   *writeNewStateDescription*: Existing implementations rarely notice that this
+    callback is invoked after *all* entities are restored. Instead, they check
+    if necessary data are all restored in the `restoreEntity` (e.g.
     [BatteryBackupHelper](https://cs.android.com/android/platform/superproject/main/+/main:packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryBackupHelper.java;l=144;drc=cca804e1ed504e2d477be1e3db00fb881ca32736)),
     which is not robust sometimes.
 
-This library provides more clear API and offers some improvements:
+The datastore library will mitigate these problems by providing alternative
+APIs. For instance, library users make use of `InputStream` / `OutputStream` to
+back up and restore data directly.
 
--   The implementation only needs to focus on the `BackupRestoreEntity`
-    interface. The `InputStream` of restore will ensure bounded data are read,
-    and close the stream will be no-op.
--   The library computes checksum of the backup data automatically, so that
-    unchanged data will not be sent to Android backup system.
+### Observer pattern
+
+In the current implementation, the Android backup framework requires a manual
+call to
+[BackupManager.dataChanged](https://developer.android.com/reference/android/app/backup/BackupManager#dataChanged\(\)).
+However, it's often observed that this API call is forgotten when using
+`SharedPreferences`. Additionally, there's a common need to log metrics when
+data changed. To address these limitations, datastore API employed the observer
+pattern.
+
+### API design and advantages
+
+Datastore must extend the `BackupRestoreStorage` class (subclass of
+[BackupHelper](https://developer.android.com/reference/android/app/backup/BackupHelper)).
+The data in a datastore is group by entity, which is represented by
+`BackupRestoreEntity`. Basically, a datastore implementation only needs to focus
+on the `BackupRestoreEntity`.
+
+If the datastore is key-value based (e.g. `SharedPreferences`), implements the
+`KeyedObservable` interface to offer fine-grained observer. Otherwise,
+implements `Observable`. There are builtin thread-safe implementations of the
+two interfaces (`KeyedDataObservable` / `DataObservable`). If it is Kotlin, use
+delegation to simplify the code.
+
+Keep in mind that the implementation should call `KeyedObservable.notifyChange`
+/ `Observable.notifyChange` whenever internal data is changed, so that the
+registered observer will be notified properly.
+
+For `SharedPreferences` use case, leverage the `SharedPreferencesStorage`
+directly. To back up other file based storage, extend the
+`BackupRestoreFileStorage` class.
+
+Here are some highlights of the library:
+
+-   The restore `InputStream` will ensure bounded data are read, and close the
+    stream is no-op. That being said, all entities are isolated.
+-   Data checksum is computed automatically, unchanged data will not be sent to
+    Android backup system.
 -   Data compression is supported:
     -   ZIP best compression is enabled by default, no extra effort needs to be
         taken.
@@ -67,98 +105,159 @@
     successfully restored in those older versions. This is achieved by extending
     the `BackupRestoreFileStorage` class, and `BackupRestoreFileArchiver` will
     treat each file as an entity and do the backup / restore.
--   Manual `BackupManager.dataChanged` call is unnecessary now, the library will
-    do the invocation (see next section).
+-   Manual `BackupManager.dataChanged` call is unnecessary now, the framework
+    will invoke the API automatically.
 
-### Observer pattern
+## Usages
 
-Manual `BackupManager.dataChanged` call is required by current backup framework.
-In practice, it is found that `SharedPreferences` usages foget to invoke the
-API. Besides, there are common use cases to log metrics when data is changed.
-Consequently, observer pattern is employed to resolve the issues.
+This section provides [examples](example/ExampleStorage.kt) of datastore.
 
-If the datastore is key-value based (e.g. `SharedPreferences`), implements the
-`KeyedObservable` interface to offer fine-grained observer. Otherwise,
-implements `Observable`. The library provides thread-safe implementations
-(`KeyedDataObservable` / `DataObservable`), and Kotlin delegation will be
-helpful.
-
-Keep in mind that the implementation should call `KeyedObservable.notifyChange`
-/ `Observable.notifyChange` whenever internal data is changed, so that the
-registered observer will be notified properly.
-
-## Usage and example
-
-For `SharedPreferences` use case, leverage the `SharedPreferencesStorage`. To
-back up other file based storage, extend the `BackupRestoreFileStorage` class.
-
-Here is an example of customized datastore, which has a string to back up:
+Here is a datastore with a string data:
 
 ```kotlin
-class MyDataStore : ObservableBackupRestoreStorage() {
-    // Another option is make it a StringEntity type and maintain a String field inside StringEntity
-    @Volatile // backup/restore happens on Binder thread
-    var data: String? = null
-        private set
+class ExampleStorage : ObservableBackupRestoreStorage() {
+  @Volatile // field is manipulated by multiple threads, synchronization might be needed
+  var data: String? = null
+    private set
 
-    fun setData(data: String?) {
-        this.data = data
-        notifyChange(ChangeReason.UPDATE)
+  @AnyThread
+  fun setData(data: String?) {
+    this.data = data
+    // call notifyChange to trigger backup and metrics logging whenever data is changed
+    if (data != null) {
+      notifyChange(ChangeReason.UPDATE)
+    } else {
+      notifyChange(ChangeReason.DELETE)
     }
-
-    override val name: String
-        get() = "MyData"
-
-    override fun createBackupRestoreEntities(): List<BackupRestoreEntity> =
-        listOf(StringEntity("data"))
-
-    private inner class StringEntity(override val key: String) : BackupRestoreEntity {
-        override fun backup(
-            backupContext: BackupContext,
-            outputStream: OutputStream,
-        ) =
-            if (data != null) {
-                outputStream.write(data!!.toByteArray(UTF_8))
-                EntityBackupResult.UPDATE
-            } else {
-                EntityBackupResult.DELETE
-            }
-
-        override fun restore(restoreContext: RestoreContext, inputStream: InputStream) {
-            data = String(inputStream.readAllBytes(), UTF_8)
-            // NOTE: The library will call notifyChange(ChangeReason.RESTORE) for you
-        }
-    }
-
-    override fun onRestoreFinished() {
-        // TODO: Update state with the restored data. Use this callback instead "restore()" in case
-        //       the restore action involves several entities.
-        // NOTE: The library will call notifyChange(ChangeReason.RESTORE) for you
-    }
-}
-```
-
-In the application class:
-
-```kotlin
-class MyApplication : Application() {
-  override fun onCreate() {
-    super.onCreate();
-    BackupRestoreStorageManager.getInstance(this).add(MyDataStore());
   }
-}
-```
 
-In the custom `BackupAgentHelper` class:
+  override val name: String
+    get() = "ExampleStorage"
 
-```kotlin
-class MyBackupAgentHelper : BackupAgentHelper() {
-  override fun onCreate() {
-    BackupRestoreStorageManager.getInstance(this).addBackupAgentHelpers(this);
+  override fun createBackupRestoreEntities(): List<BackupRestoreEntity> =
+    listOf(StringEntity("data"))
+
+  override fun enableRestore(): Boolean {
+    return true // check condition like flag, environment, etc.
+  }
+
+  override fun enableBackup(backupContext: BackupContext): Boolean {
+    return true // check condition like flag, environment, etc.
+  }
+
+  @BinderThread
+  private inner class StringEntity(override val key: String) : BackupRestoreEntity {
+    override fun backup(backupContext: BackupContext, outputStream: OutputStream) =
+      if (data != null) {
+        outputStream.write(data!!.toByteArray(UTF_8))
+        EntityBackupResult.UPDATE
+      } else {
+        EntityBackupResult.DELETE // delete existing backup data
+      }
+
+    override fun restore(restoreContext: RestoreContext, inputStream: InputStream) {
+      // DO NOT call setData API here, which will trigger notifyChange unexpectedly.
+      // Under the hood, the datastore library will call notifyChange(ChangeReason.RESTORE)
+      // later to notify observers.
+      data = String(inputStream.readBytes(), UTF_8)
+      // Handle restored data in onRestoreFinished() callback
+    }
   }
 
   override fun onRestoreFinished() {
-    BackupRestoreStorageManager.getInstance(this).onRestoreFinished();
+    // TODO: Update state with the restored data. Use this callback instead of "restore()" in
+    //       case the restore action involves several entities.
+    // NOTE: The library will call notifyChange(ChangeReason.RESTORE) for you
   }
 }
 ```
+
+And this is a datastore with key value data:
+
+```kotlin
+class ExampleKeyValueStorage :
+  BackupRestoreStorage(), KeyedObservable<String> by KeyedDataObservable() {
+  // thread safe data structure
+  private val map = ConcurrentHashMap<String, String>()
+
+  override val name: String
+    get() = "ExampleKeyValueStorage"
+
+  fun updateData(key: String, value: String?) {
+    if (value != null) {
+      map[key] = value
+      notifyChange(ChangeReason.UPDATE)
+    } else {
+      map.remove(key)
+      notifyChange(ChangeReason.DELETE)
+    }
+  }
+
+  override fun createBackupRestoreEntities(): List<BackupRestoreEntity> =
+    listOf(createMapBackupRestoreEntity())
+
+  private fun createMapBackupRestoreEntity() =
+    object : BackupRestoreEntity {
+      override val key: String
+        get() = "map"
+
+      override fun backup(
+        backupContext: BackupContext,
+        outputStream: OutputStream,
+      ): EntityBackupResult {
+        // Use TreeMap to achieve predictable and stable order, so that data will not be
+        // updated to Android backup backend if there is only order change.
+        val copy = TreeMap(map)
+        if (copy.isEmpty()) return EntityBackupResult.DELETE
+        val dataOutputStream = DataOutputStream(outputStream)
+        dataOutputStream.writeInt(copy.size)
+        for ((key, value) in copy) {
+          dataOutputStream.writeUTF(key)
+          dataOutputStream.writeUTF(value)
+        }
+        return EntityBackupResult.UPDATE
+      }
+
+      override fun restore(restoreContext: RestoreContext, inputStream: InputStream) {
+        val dataInputString = DataInputStream(inputStream)
+        repeat(dataInputString.readInt()) {
+          val key = dataInputString.readUTF()
+          val value = dataInputString.readUTF()
+          map[key] = value
+        }
+      }
+    }
+}
+```
+
+All the datastore should be added in the application class:
+
+```kotlin
+class ExampleApplication : Application() {
+  override fun onCreate() {
+    super.onCreate()
+    BackupRestoreStorageManager.getInstance(this)
+      .add(ExampleStorage(), ExampleKeyValueStorage())
+  }
+}
+```
+
+Additionally, inject datastore to the custom `BackupAgentHelper` class:
+
+```kotlin
+class ExampleBackupAgent : BackupAgentHelper() {
+  override fun onCreate() {
+    super.onCreate()
+    BackupRestoreStorageManager.getInstance(this).addBackupAgentHelpers(this)
+  }
+
+  override fun onRestoreFinished() {
+    BackupRestoreStorageManager.getInstance(this).onRestoreFinished()
+  }
+}
+```
+
+## Development
+
+Please preserve the code coverage ratio during development. The current line
+coverage is **100% (444/444)** and branch coverage is **93.6% (176/188)**.
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt
index 817ee4c..6720e5c 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt
@@ -23,7 +23,11 @@
 import java.io.InputStream
 import java.io.OutputStream
 
-/** Entity for back up and restore. */
+/**
+ * Entity for back up and restore.
+ *
+ * Note that backup/restore callback is invoked on the binder thread.
+ */
 interface BackupRestoreEntity {
     /**
      * Key of the entity.
@@ -45,9 +49,12 @@
     /**
      * Backs up the entity.
      *
+     * Back up data in predictable order (e.g. use `TreeMap` instead of `HashMap`), otherwise data
+     * will be backed up needlessly.
+     *
      * @param backupContext context for backup
      * @param outputStream output stream to back up data
-     * @return false if backup file is deleted, otherwise true
+     * @return backup result
      */
     @BinderThread
     @Throws(IOException::class)
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
index 935f9cc..284c97b 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
@@ -22,6 +22,7 @@
 import android.app.backup.BackupHelper
 import android.os.ParcelFileDescriptor
 import android.util.Log
+import androidx.annotation.BinderThread
 import androidx.annotation.VisibleForTesting
 import androidx.collection.MutableScatterMap
 import com.google.common.io.ByteStreams
@@ -38,16 +39,22 @@
 import java.util.zip.CheckedInputStream
 import java.util.zip.CheckedOutputStream
 import java.util.zip.Checksum
+import javax.annotation.concurrent.ThreadSafe
 
 internal const val LOG_TAG = "BackupRestoreStorage"
 
 /**
- * Storage with backup and restore support. Subclass must implement either [Observable] or
- * [KeyedObservable] interface.
+ * Storage with backup and restore support.
+ *
+ * Subclass MUST
+ * - implement either [Observable] or [KeyedObservable] interface.
+ * - be thread safe, backup/restore happens on Binder thread, while general data read/write
+ *   operations occur on other threads.
  *
  * The storage is identified by a unique string [name] and data set is split into entities
  * ([BackupRestoreEntity]).
  */
+@ThreadSafe
 abstract class BackupRestoreStorage : BackupHelper {
     /**
      * A unique string used to disambiguate the various storages within backup agent.
@@ -68,7 +75,7 @@
     @VisibleForTesting internal var entities: List<BackupRestoreEntity>? = null
 
     /** Entities to back up and restore. */
-    abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity>
+    @BinderThread abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity>
 
     /** Default codec used to encode/decode the entity data. */
     open fun defaultCodec(): BackupCodec = BackupZipCodec.BEST_COMPRESSION
@@ -134,7 +141,11 @@
         Log.i(LOG_TAG, "[$name] Backup end")
     }
 
-    /** Returns if backup is enabled. */
+    /**
+     * Returns if backup is enabled.
+     *
+     * If disabled, [performBackup] will be no-op, all entities backup are skipped.
+     */
     open fun enableBackup(backupContext: BackupContext): Boolean = true
 
     open fun wrapBackupOutputStream(codec: BackupCodec, outputStream: OutputStream): OutputStream {
@@ -172,7 +183,11 @@
     private fun ensureEntities(): List<BackupRestoreEntity> =
         entities ?: createBackupRestoreEntities().also { entities = it }
 
-    /** Returns if restore is enabled. */
+    /**
+     * Returns if restore is enabled.
+     *
+     * If disabled, [restoreEntity] will be no-op, all entities restore are skipped.
+     */
     open fun enableRestore(): Boolean = true
 
     open fun wrapRestoreInputStream(
@@ -188,12 +203,13 @@
     }
 
     final override fun writeNewStateDescription(newState: ParcelFileDescriptor) {
+        if (!enableRestore()) return
         entities = null // clear to reduce memory footprint
         newState.writeAndClearEntityStates()
         onRestoreFinished()
     }
 
-    /** Callbacks when restore finished. */
+    /** Callbacks when entity data are all restored. */
     open fun onRestoreFinished() {}
 
     @VisibleForTesting
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt
index 99998ff..26534ba 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt
@@ -248,6 +248,15 @@
     }
 
     @Test
+    fun writeNewStateDescription_restoreDisabled() {
+        val storage = spy(TestStorage().apply { enabled = false })
+        temporaryFolder.newFile().toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+            storage.writeNewStateDescription(it)
+        }
+        verify(storage, never()).onRestoreFinished()
+    }
+
+    @Test
     fun backupAndRestore() {
         val storage = spy(TestStorage(entity1, entity2))
         val backupAgentHelper = BackupAgentHelper()
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index 05507e0..493818b 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -80,14 +80,15 @@
                 continue;
             }
             final URLSpan urlSpan = (URLSpan) clickable;
-            if (!urlSpan.getURL().startsWith(INTENT_URL_PREFIX)) {
+            final String url = urlSpan.getURL();
+            if (url == null || !url.startsWith(INTENT_URL_PREFIX)) {
                 continue;
             }
             final int start = spannable.getSpanStart(urlSpan);
             final int end = spannable.getSpanEnd(urlSpan);
             spannable.removeSpan(urlSpan);
             try {
-                final Intent intent = Intent.parseUri(urlSpan.getURL(), Intent.URI_INTENT_SCHEME);
+                final Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
                 final ClickableSpan clickableSpan =
                         new ClickableSpan() {
                             @Override
@@ -98,7 +99,7 @@
                         };
                 spannable.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
             } catch (URISyntaxException e) {
-                Log.e(TAG, "Invalid URI " + urlSpan.getURL(), e);
+                Log.e(TAG, "Invalid URI " + url, e);
             }
         }
         title.setText(spannable);
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index 6407810..c3a91a2 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -21,6 +21,7 @@
         "SettingsLibColor",
         "androidx.preference_preference",
         "lottie",
+        "settingslib_illustrationpreference_flags_lib",
     ],
 
     sdk_version: "system_current",
@@ -31,3 +32,24 @@
         "com.android.permission",
     ],
 }
+
+aconfig_declarations {
+    name: "settingslib_illustrationpreference_flags",
+    package: "com.android.settingslib.widget.flags",
+    container: "system",
+    srcs: [
+        "aconfig/illustrationpreference.aconfig",
+    ],
+}
+
+java_aconfig_library {
+    name: "settingslib_illustrationpreference_flags_lib",
+    aconfig_declarations: "settingslib_illustrationpreference_flags",
+
+    min_sdk_version: "30",
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.permission",
+    ],
+}
diff --git a/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig b/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig
new file mode 100644
index 0000000..e566d89
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.settingslib.widget.flags"
+container: "system"
+
+flag {
+    name: "auto_hide_empty_lottie_res"
+    namespace: "android_settings"
+    description: "Hides IllustrationPreference when Lottie resource is an empty file"
+    bug: "337873972"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 815a101..bbf0315 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -39,12 +39,14 @@
 import androidx.preference.PreferenceViewHolder;
 import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
 
+import com.android.settingslib.widget.flags.Flags;
 import com.android.settingslib.widget.preference.illustration.R;
 
 import com.airbnb.lottie.LottieAnimationView;
 import com.airbnb.lottie.LottieDrawable;
 
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.InputStream;
 
 /**
@@ -142,7 +144,7 @@
         illustrationFrame.setLayoutParams(lp);
 
         illustrationView.setCacheComposition(mCacheComposition);
-        handleImageWithAnimation(illustrationView);
+        handleImageWithAnimation(illustrationView, illustrationFrame);
         handleImageFrameMaxHeight(backgroundView, illustrationView);
 
         if (mIsAutoScale) {
@@ -332,7 +334,8 @@
         }
     }
 
-    private void handleImageWithAnimation(LottieAnimationView illustrationView) {
+    private void handleImageWithAnimation(LottieAnimationView illustrationView,
+            ViewGroup container) {
         if (mImageDrawable != null) {
             resetAnimations(illustrationView);
             illustrationView.setImageDrawable(mImageDrawable);
@@ -356,6 +359,25 @@
         }
 
         if (mImageResId > 0) {
+            if (Flags.autoHideEmptyLottieRes()) {
+                // Check if resource is empty
+                try (InputStream is = illustrationView.getResources()
+                        .openRawResource(mImageResId)) {
+                    int check = is.read();
+                    // -1 = end of stream. if first read is end of stream, then file is empty
+                    if (check == -1) {
+                        illustrationView.setVisibility(View.GONE);
+                        container.setVisibility(View.GONE);
+                        return;
+                    }
+                } catch (IOException e) {
+                    Log.w(TAG, "Unable to open Lottie raw resource", e);
+                }
+
+                illustrationView.setVisibility(View.VISIBLE);
+                container.setVisibility(View.VISIBLE);
+            }
+
             resetAnimations(illustrationView);
             illustrationView.setImageResource(mImageResId);
             final Drawable drawable = illustrationView.getDrawable();
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml b/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml
index 4ced9f2..cece966 100644
--- a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml
+++ b/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml
@@ -16,8 +16,8 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:color="@color/settingslib_materialColorSecondaryContainer"/>
-    <item android:state_selected="true" android:color="@color/settingslib_materialColorSecondaryContainer"/>
-    <item android:state_activated="true" android:color="@color/settingslib_materialColorSecondaryContainer"/>
-    <item android:color="@color/settingslib_materialColorSurfaceContainerHighest"/> <!-- not selected -->
+    <item android:state_pressed="true" android:color="@color/settingslib_materialColorSurfaceContainerHigh"/>
+    <item android:state_selected="true" android:color="@color/settingslib_materialColorSurfaceContainerHigh"/>
+    <item android:state_activated="true" android:color="@color/settingslib_materialColorSurfaceContainerHigh"/>
+    <item android:color="@color/settingslib_materialColorSurfaceBright"/> <!-- not selected -->
 </selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml
index 285ab73..eba9c2c 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml
@@ -19,12 +19,12 @@
     <item
         android:left="?android:attr/listPreferredItemPaddingStart"
         android:right="?android:attr/listPreferredItemPaddingEnd"
-        android:top="1dp">
+        android:top="2dp">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_preference_bg_color" />
             <corners
-                android:radius="?android:attr/dialogCornerRadius" />
+                android:radius="@dimen/settingslib_preference_corner_radius" />
         </shape>
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml
index e417307..5c60f37 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml
@@ -19,15 +19,15 @@
     <item
         android:left="?android:attr/listPreferredItemPaddingStart"
         android:right="?android:attr/listPreferredItemPaddingEnd"
-        android:top="1dp">
+        android:top="2dp">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_preference_bg_color" />
             <corners
-                android:topLeftRadius="0dp"
-                android:bottomLeftRadius="?android:attr/dialogCornerRadius"
-                android:topRightRadius="0dp"
-                android:bottomRightRadius="?android:attr/dialogCornerRadius" />
+                android:topLeftRadius="4dp"
+                android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
+                android:topRightRadius="4dp"
+                android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" />
         </shape>
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml
new file mode 100644
index 0000000..de64efd
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:left="?android:attr/listPreferredItemPaddingStart"
+        android:right="?android:attr/listPreferredItemPaddingEnd"
+        android:top="2dp">
+        <shape android:shape="rectangle">
+            <solid
+                android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+            <corners
+                android:topLeftRadius="4dp"
+                android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
+                android:topRightRadius="4dp"
+                android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml
index e964657..dd70f4f 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml
@@ -19,12 +19,12 @@
     <item
         android:left="?android:attr/listPreferredItemPaddingStart"
         android:right="?android:attr/listPreferredItemPaddingEnd"
-        android:top="1dp">
+        android:top="2dp">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_preference_bg_color" />
             <corners
-                android:radius="1dp" />
+                android:radius="4dp" />
         </shape>
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml
new file mode 100644
index 0000000..fffc6c8
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:left="?android:attr/listPreferredItemPaddingStart"
+        android:right="?android:attr/listPreferredItemPaddingEnd"
+        android:top="2dp">
+        <shape android:shape="rectangle">
+            <solid
+                android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+            <corners
+                android:radius="4dp" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml
new file mode 100644
index 0000000..f83e3b1
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:left="?android:attr/listPreferredItemPaddingStart"
+        android:right="?android:attr/listPreferredItemPaddingEnd"
+        android:top="2dp">
+        <shape android:shape="rectangle">
+            <solid
+                android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+            <corners
+                android:radius="@dimen/settingslib_preference_corner_radius" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml
index a9d69c2..ab79d18 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml
@@ -19,15 +19,15 @@
     <item
         android:left="?android:attr/listPreferredItemPaddingStart"
         android:right="?android:attr/listPreferredItemPaddingEnd"
-        android:top="1dp">
+        android:top="2dp">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_preference_bg_color" />
             <corners
-                android:topLeftRadius="?android:attr/dialogCornerRadius"
-                android:bottomLeftRadius="0dp"
-                android:topRightRadius="?android:attr/dialogCornerRadius"
-                android:bottomRightRadius="0dp" />
+                android:topLeftRadius="@dimen/settingslib_preference_corner_radius"
+                android:bottomLeftRadius="4dp"
+                android:topRightRadius="@dimen/settingslib_preference_corner_radius"
+                android:bottomRightRadius="4dp" />
         </shape>
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml
new file mode 100644
index 0000000..112ec73
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:left="?android:attr/listPreferredItemPaddingStart"
+        android:right="?android:attr/listPreferredItemPaddingEnd"
+        android:top="2dp">
+        <shape android:shape="rectangle">
+            <solid
+                android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+            <corners
+                android:topLeftRadius="@dimen/settingslib_preference_corner_radius"
+                android:bottomLeftRadius="4dp"
+                android:topRightRadius="@dimen/settingslib_preference_corner_radius"
+                android:bottomRightRadius="4dp" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
index 221e8f5..94ff02d 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
@@ -37,8 +37,11 @@
     <!-- Material next track off color-->
     <color name="settingslib_track_off_color">@color/settingslib_materialColorSurfaceContainerHighest</color>
 
+    <!-- Dialog text button color. -->
+    <color name="settingslib_dialog_accent">@color/settingslib_materialColorPrimary</color>
+
     <!-- Dialog background color. -->
-    <color name="settingslib_dialog_background">@color/settingslib_materialColorSurfaceContainer</color>
+    <color name="settingslib_dialog_background">@color/settingslib_materialColorSurfaceContainerHigh</color>
 
     <color name="settingslib_colorSurfaceHeader">@color/settingslib_materialColorSurfaceVariant</color>
 
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
index dc2d3dc..8b95016 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
@@ -37,8 +37,11 @@
     <!-- Material next track off color-->
     <color name="settingslib_track_off_color">@color/settingslib_materialColorSurfaceContainerHighest</color>
 
+    <!-- Dialog text button color. -->
+    <color name="settingslib_dialog_accent">@color/settingslib_materialColorPrimary</color>
+
     <!-- Dialog background color. -->
-    <color name="settingslib_dialog_background">@color/settingslib_materialColorSurfaceContainer</color>
+    <color name="settingslib_dialog_background">@color/settingslib_materialColorSurfaceContainerHigh</color>
 
     <!-- Material next track outline color-->
     <color name="settingslib_track_online_color">@color/settingslib_switch_track_outline_color</color>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml
new file mode 100644
index 0000000..d783956
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <dimen name="settingslib_preference_corner_radius">20dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 4147813..45667f5 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
 
 allprojects {
     extra["androidTop"] = androidTop
-    extra["jetpackComposeVersion"] = "1.7.0-alpha05"
+    extra["jetpackComposeVersion"] = "1.7.0-alpha08"
 }
 
 subprojects {
@@ -41,7 +41,7 @@
 
             defaultConfig {
                 minSdk = 21
-                targetSdk = 34
+                targetSdk = 35
             }
         }
 
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 6344501..4aa57b3 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -53,17 +53,17 @@
 
 dependencies {
     api(project(":SettingsLibColor"))
-    api("androidx.appcompat:appcompat:1.7.0-alpha03")
+    api("androidx.appcompat:appcompat:1.7.0-beta01")
     api("androidx.slice:slice-builders:1.1.0-alpha02")
     api("androidx.slice:slice-core:1.1.0-alpha02")
     api("androidx.slice:slice-view:1.1.0-alpha02")
-    api("androidx.compose.material3:material3:1.3.0-alpha03")
+    api("androidx.compose.material3:material3:1.3.0-alpha06")
     api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
     api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
     api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
     api("androidx.lifecycle:lifecycle-livedata-ktx")
     api("androidx.lifecycle:lifecycle-runtime-compose")
-    api("androidx.navigation:navigation-compose:2.8.0-alpha05")
+    api("androidx.navigation:navigation-compose:2.8.0-alpha08")
     api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
     api("com.google.android.material:material:1.11.0")
     debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index da1ee77..e867a8f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -21,6 +21,7 @@
 import android.util.Log
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
 import androidx.annotation.VisibleForTesting
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
@@ -30,7 +31,6 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.Modifier
-import androidx.core.view.WindowCompat
 import androidx.navigation.NavBackStackEntry
 import androidx.navigation.NavGraph.Companion.findStartDestination
 import androidx.navigation.NavGraphBuilder
@@ -82,7 +82,7 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         setTheme(R.style.Theme_SpaLib)
         super.onCreate(savedInstanceState)
-        WindowCompat.setDecorFitsSystemWindows(window, false)
+        enableEdgeToEdge()
         spaEnvironment.logger.message(TAG, "onCreate", category = LogCategory.FRAMEWORK)
 
         setContent {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt
index 88ba4b0..1a10bf0 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt
@@ -27,14 +27,13 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.LinkAnnotation
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.text.font.FontStyle
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextDecoration
 
-const val URL_SPAN_TAG = "URL_SPAN_TAG"
-
 @Composable
 fun annotatedStringResource(@StringRes id: Int): AnnotatedString {
     val resources = LocalContext.current.resources
@@ -97,12 +96,9 @@
     start: Int,
     end: Int,
 ) {
-    addStyle(
-        SpanStyle(color = urlSpanColor, textDecoration = TextDecoration.Underline),
-        start,
-        end,
+    val url = LinkAnnotation.Url(
+        url = urlSpan.url,
+        style = SpanStyle(color = urlSpanColor, textDecoration = TextDecoration.Underline),
     )
-    if (!urlSpan.url.isNullOrEmpty()) {
-        addStringAnnotation(URL_SPAN_TAG, urlSpan.url, start, end)
-    }
+    addLink(url, start, end)
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index 36cd136..9a344c3 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -35,6 +35,7 @@
 import androidx.compose.foundation.layout.WindowInsetsSides
 import androidx.compose.foundation.layout.only
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
 import androidx.compose.foundation.layout.windowInsetsPadding
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.LocalContentColor
@@ -42,11 +43,11 @@
 import androidx.compose.material3.ProvideTextStyle
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.material3.TopAppBarState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.NonRestartableComposable
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.mutableFloatStateOf
@@ -79,7 +80,12 @@
 import kotlin.math.max
 import kotlin.math.roundToInt
 
-@OptIn(ExperimentalMaterial3Api::class)
+private val windowInsets: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsets.safeDrawing
+        .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top)
+
 @Composable
 internal fun CustomizedTopAppBar(
     title: @Composable () -> Unit,
@@ -91,7 +97,7 @@
         titleTextStyle = MaterialTheme.typography.titleMedium,
         navigationIcon = navigationIcon,
         actions = actions,
-        windowInsets = TopAppBarDefaults.windowInsets,
+        windowInsets = windowInsets,
         colors = topAppBarColors(),
     )
 }
@@ -118,7 +124,7 @@
         navigationIcon = navigationIcon,
         actions = actions,
         colors = topAppBarColors(),
-        windowInsets = TopAppBarDefaults.windowInsets,
+        windowInsets = windowInsets,
         pinnedHeight = ContainerHeight,
         scrollBehavior = scrollBehavior,
     )
@@ -336,7 +342,7 @@
         Modifier.draggable(
             orientation = Orientation.Vertical,
             state = rememberDraggableState { delta ->
-                scrollBehavior.state.heightOffset = scrollBehavior.state.heightOffset + delta
+                scrollBehavior.state.heightOffset += delta
             },
             onDragStopped = { velocity ->
                 settleAppBar(
@@ -411,6 +417,7 @@
  * (leading icon), a title (header), and action icons (trailing icons). Note that the navigation and
  * the actions are optional.
  *
+ * @param modifier a [Modifier]
  * @param heightPx the total height this layout is capped to
  * @param navigationIconContentColor the content color that will be applied via a
  * [LocalContentColor] when composing the navigation icon
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
index a49b358..4a7937a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
@@ -22,9 +22,11 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material3.ExperimentalMaterial3Api
@@ -92,6 +94,7 @@
             )
         },
         containerColor = MaterialTheme.colorScheme.settingsBackground,
+        contentWindowInsets = WindowInsets.safeDrawing,
     ) { paddingValues ->
         Box(
             Modifier
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
index af7a146..4cf741e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
@@ -23,7 +23,9 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Scaffold
@@ -57,6 +59,7 @@
         modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
         topBar = { SettingsTopAppBar(title, scrollBehavior, actions) },
         containerColor = MaterialTheme.colorScheme.settingsBackground,
+        contentWindowInsets = WindowInsets.safeDrawing,
     ) { paddingValues ->
         Box(Modifier.padding(paddingValues.horizontalValues())) {
             content(paddingValues.verticalValues())
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
index fc40930..4726dad 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
@@ -21,7 +21,9 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
@@ -55,7 +57,10 @@
     content: @Composable () -> Unit,
 ) {
     ActivityTitle(title)
-    Scaffold(containerColor = MaterialTheme.colorScheme.settingsBackground) { innerPadding ->
+    Scaffold(
+        containerColor = MaterialTheme.colorScheme.settingsBackground,
+        contentWindowInsets = WindowInsets.safeDrawing,
+    ) { innerPadding ->
         BoxWithConstraints(
             Modifier
                 .padding(innerPadding)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/AnnotatedText.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/AnnotatedText.kt
index 82ac7e3..f864fa9 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/AnnotatedText.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/AnnotatedText.kt
@@ -17,26 +17,17 @@
 package com.android.settingslib.spa.widget.ui
 
 import androidx.annotation.StringRes
-import androidx.compose.foundation.text.ClickableText
 import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.LocalUriHandler
-import com.android.settingslib.spa.framework.util.URL_SPAN_TAG
 import com.android.settingslib.spa.framework.util.annotatedStringResource
 
 @Composable
 fun AnnotatedText(@StringRes id: Int) {
-    val uriHandler = LocalUriHandler.current
-    val annotatedString = annotatedStringResource(id)
-    ClickableText(
-        text = annotatedString,
+    Text(
+        text = annotatedStringResource(id),
         style = MaterialTheme.typography.bodyMedium.copy(
             color = MaterialTheme.colorScheme.onSurfaceVariant,
         ),
-    ) { offset ->
-        // Gets the url at the clicked position.
-        annotatedString.getStringAnnotations(URL_SPAN_TAG, offset, offset)
-            .firstOrNull()
-            ?.let { uriHandler.openUri(it.item) }
-    }
+    )
 }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt
index 9928355..612f9e5 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt
@@ -19,6 +19,7 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.LinkAnnotation
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.font.FontStyle
 import androidx.compose.ui.text.font.FontWeight
@@ -36,18 +37,23 @@
     val composeTestRule = createComposeRule()
 
     @Test
-    fun testAnnotatedStringResource() {
+    fun annotatedStringResource() {
         composeTestRule.setContent {
             val annotatedString =
                 annotatedStringResource(R.string.test_annotated_string_resource)
 
-            val annotations = annotatedString.getStringAnnotations(0, annotatedString.length)
+            val annotations = annotatedString.getLinkAnnotations(0, annotatedString.length)
             assertThat(annotations).containsExactly(
                 AnnotatedString.Range(
-                    item = "https://www.android.com/",
+                    item = LinkAnnotation.Url(
+                        url = "https://www.android.com/",
+                        style = SpanStyle(
+                            color = MaterialTheme.colorScheme.primary,
+                            textDecoration = TextDecoration.Underline,
+                        ),
+                    ),
                     start = 31,
                     end = 35,
-                    tag = URL_SPAN_TAG,
                 )
             )
 
@@ -57,14 +63,6 @@
                     start = 22,
                     end = 26,
                 ),
-                AnnotatedString.Range(
-                    item = SpanStyle(
-                        color = MaterialTheme.colorScheme.primary,
-                        textDecoration = TextDecoration.Underline,
-                    ),
-                    start = 31,
-                    end = 35,
-                ),
             )
         }
     }
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 89f54d9..9c0d29d 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -62,3 +62,10 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+  name: "allow_all_widgets_on_lockscreen_by_default"
+  namespace: "systemui"
+  description: "Allow all widgets on the lock screen by default."
+  bug: "328261690"
+}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 1594e8e..ad5337c 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -105,9 +105,9 @@
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Regs: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktief"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Gestoor"</string>
-    <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktief (net linkerkant)"</string>
-    <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktief (net regterkant)"</string>
-    <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktief (linkerkant en regterkant)"</string>
+    <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktief (net links)"</string>
+    <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktief (net regs)"</string>
+    <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktief (links en regs)"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktief (net media). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktiewe (net media). L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Gekoppel (steun oudiodeling). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 0cfae92..1a0fec5d 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -125,7 +125,7 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
-    <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Acceso a contactos e historial de llamadas"</string>
+    <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Acceso a historial de llamadas y contactos"</string>
     <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Se usará la información para anuncios de llamadas y más"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string>
@@ -281,7 +281,7 @@
     <string name="keep_screen_on_summary" msgid="1510731514101925829">"La pantalla nunca quedará inactiva mientras el dispositivo se esté cargando"</string>
     <string name="bt_hci_snoop_log" msgid="7291287955649081448">"Registro de Bluetooth HCI"</string>
     <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Capturar paquetes de Bluetooth (activa/desactiva el Bluetooth después de cambiar esta configuración)"</string>
-    <string name="oem_unlock_enable" msgid="5334869171871566731">"Desbloqueo de OEM"</string>
+    <string name="oem_unlock_enable" msgid="5334869171871566731">"Desbloqueo para OEM"</string>
     <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Permitir que el cargador de inicio se desbloquee"</string>
     <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"¿Permitir desbloqueo de OEM?"</string>
     <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"ADVERTENCIA: Las funciones de protección de dispositivos no funcionarán en este dispositivo mientras esta configuración esté activada."</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 497fbbb..c330b76 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -294,7 +294,7 @@
     <string name="wifi_scan_throttling" msgid="2985624788509913617">"Wifi-sareen bilaketaren moteltzea"</string>
     <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wifi-konexioetan iraunkorrak ez diren MAC helbideak ausaz antolatzea"</string>
     <string name="mobile_data_always_on" msgid="8275958101875563572">"Datu-konexioa beti aktibo"</string>
-    <string name="tethering_hardware_offload" msgid="4116053719006939161">"Konexioa partekatzeko hardwarearen azelerazioa"</string>
+    <string name="tethering_hardware_offload" msgid="4116053719006939161">"Konexioa partekatzeko hardwarearen bizkortzea"</string>
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Erakutsi Bluetooth bidezko gailuak izenik gabe"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desgaitu bolumen absolutua"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gaitu Gabeldorsche"</string>
@@ -339,7 +339,7 @@
     <string name="allow_mock_location_summary" msgid="179780881081354579">"Onartu kokapen faltsuak"</string>
     <string name="debug_view_attributes" msgid="3539609843984208216">"Gaitu ikuspegiaren atributuak ikuskatzeko aukera"</string>
     <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Mantendu datu-konexioa beti aktibo, baita wifi-konexioa aktibo dagoenean ere (sare batetik bestera bizkor aldatu ahal izateko)."</string>
-    <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Erabilgarri badago, erabili konexioa partekatzeko hardwarearen azelerazioa"</string>
+    <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Erabilgarri badago, erabili konexioa partekatzeko hardwarearen bizkortzea"</string>
     <string name="adb_warning_title" msgid="7708653449506485728">"USB bidezko arazketa onartu?"</string>
     <string name="adb_warning_message" msgid="8145270656419669221">"USB bidezko arazketa garapen-xedeetarako soilik dago diseinatuta. Erabil ezazu ordenagailuaren eta gailuaren artean datuak kopiatzeko, aplikazioak gailuan jakinarazi gabe instalatzeko eta erregistro-datuak irakurtzeko."</string>
     <string name="adbwifi_warning_title" msgid="727104571653031865">"Hari gabeko arazketa baimendu nahi duzu?"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 5e7f998..1d331fb 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -96,8 +96,8 @@
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"متصل (بدون تلفن یا رسانه)، باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"فعال. باتری: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"فعال. باتری چپ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، باتری راست: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
-    <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"فعال. باتری چپ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
-    <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"فعال. باتری راست: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+    <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"فعال. چپ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> باتری."</string>
+    <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"فعال. راست: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> باتری."</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> شارژ باتری"</string>
     <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"باتری چپ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، باتری راست: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index e434d1e..cfb6546 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -126,7 +126,7 @@
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिवाइस"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"इंटरनेट का ऐक्सेस"</string>
     <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"संपर्क और कॉल इतिहास का ऐक्सेस दें"</string>
-    <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"इस जानकारी का इस्तेमाल, कॉल की सूचना देने और दूसरी चीज़ों के लिए किया जाएगा"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"इस जानकारी का इस्तेमाल, कॉल की सूचना देने वगैरह के लिए किया जाएगा"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इंटरनेट कनेक्शन साझाकरण"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"लेख संदेश"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"सिम का ऐक्सेस"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 4c0712c..5e3d779 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -159,7 +159,7 @@
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Չեղարկել"</string>
     <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"Զուգակցում է մուտքի թույլտվությունը դեպի ձեր կոնտակտները և զանգերի պատմությունը, երբ միացված է:"</string>
     <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Չհաջողվեց զուգակցել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ:"</string>
-    <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Չհաջողվեց զուգակցել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ սխալ PIN-ի կամ անցաբառի պատճառով:"</string>
+    <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Չհաջողվեց զուգակցել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ՝ սխալ PIN-ի կամ անցաբառի պատճառով:"</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"Հնարավոր չէ կապ հաստատել  <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ:"</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Զուգավորումը մերժվեց <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի կողմից:"</string>
     <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Համակարգիչ"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 4cd076b..be8d039 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -94,7 +94,7 @@
     <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (без телефон), ниво на батеријата <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (без аудиовизуелни содржини), ниво на батеријата <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (без телефон и аудиовизуелни содржини), ниво на батеријата <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Активно. <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батерија."</string>
+    <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Активно. Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Активно. Л: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батерија, Д: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батерија."</string>
     <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Активно. Л: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батерија."</string>
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Активно. Д: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батерија."</string>
@@ -159,7 +159,7 @@
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Откажи"</string>
     <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"Кога е поврзано, спарувањето одобрува пристап до контактите и историјата на повиците."</string>
     <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Не може да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
-    <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Не може да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g> поради погрешен PIN или лозинка."</string>
+    <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Не може да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g> поради погрешен PIN или криптографски клуч."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"Не може да комуницира со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Спарувањето е одбиено од <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Компјутер"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 42797d4..66efe8b 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -94,7 +94,7 @@
     <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Холбогдсон (утас байхгүй), батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Холбогдсон (медиа байхгүй), батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Холбогдсон (утас эсвэл медиа байхгүй), батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Идэвхтэй байна. <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батарей."</string>
+    <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Идэвхтэй байна. <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> цэнэгтэй."</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Идэвхтэй байна. З: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Б: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батарей."</string>
     <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Идэвхтэй байна. З: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батарей."</string>
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Идэвхтэй байна. Б: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батарей."</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 40d604e..8bafef3 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -157,7 +157,7 @@
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"कनेक्ट गर्नुहोस्"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"जोडी"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"रद्द गर्नुहोस्"</string>
-    <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"जब जडान हुन्छ जोडी अनुदानले तपाईँको सम्पर्कहरू पहुँच गर्छ र इतिहास सम्झाउँछ।"</string>
+    <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"जब जडान हुन्छ जोडी अनुदानले तपाईँको कन्ट्याक्टहरू पहुँच गर्छ र इतिहास सम्झाउँछ।"</string>
     <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>सँग जोडा मिलाउन सकेन"</string>
     <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"गलत PIN वा पासकीका कारणले <xliff:g id="DEVICE_NAME">%1$s</xliff:g> मा कनेक्ट गर्न सकिएन।"</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> सँग कुराकानी हुन सक्दैन।"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 422d0f2..d054d9b 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -110,11 +110,11 @@
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Ativo (esquerdo e direito)"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Ativo (apenas mídia). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Ativo (apenas mídia). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria. Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
-    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Conectado (com suporte ao compartilhamento de áudio). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
-    <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"Conectado (com suporte ao compartilhamento de áudio). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria. Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
-    <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="7707464334346454950">"Conectado (com suporte ao compartilhamento de áudio). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
-    <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"Conectado (com suporte ao compartilhamento de áudio). Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
-    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Conectado (com suporte ao compartilhamento de áudio)"</string>
+    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Conectado (aceita compartilhamento de áudio). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
+    <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"Conectado (aceita compartilhamento de áudio). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria. Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
+    <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="7707464334346454950">"Conectado (aceita compartilhamento de áudio). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
+    <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"Conectado (aceita compartilhamento de áudio). Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
+    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Conectado (aceita compartilhamento de áudio)"</string>
     <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Ativo (apenas mídia)"</string>
     <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Com suporte ao compartilhamento de áudio"</string>
     <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Ativo (apenas mídia), apenas esquerdo"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 422d0f2..d054d9b 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -110,11 +110,11 @@
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Ativo (esquerdo e direito)"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Ativo (apenas mídia). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Ativo (apenas mídia). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria. Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
-    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Conectado (com suporte ao compartilhamento de áudio). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
-    <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"Conectado (com suporte ao compartilhamento de áudio). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria. Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
-    <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="7707464334346454950">"Conectado (com suporte ao compartilhamento de áudio). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
-    <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"Conectado (com suporte ao compartilhamento de áudio). Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
-    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Conectado (com suporte ao compartilhamento de áudio)"</string>
+    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Conectado (aceita compartilhamento de áudio). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
+    <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"Conectado (aceita compartilhamento de áudio). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria. Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
+    <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="7707464334346454950">"Conectado (aceita compartilhamento de áudio). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
+    <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"Conectado (aceita compartilhamento de áudio). Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
+    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Conectado (aceita compartilhamento de áudio)"</string>
     <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Ativo (apenas mídia)"</string>
     <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Com suporte ao compartilhamento de áudio"</string>
     <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Ativo (apenas mídia), apenas esquerdo"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 54c9b0a..aaf648e 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -94,7 +94,7 @@
     <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Подключено (кроме звонков), уровень заряда батареи: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Подключено (кроме аудио), уровень заряда батареи: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Подключено (кроме звонков и аудио), уровень заряда батареи: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Используется, заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+    <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Используется, заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Используется, заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (Л), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (П)."</string>
     <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Используется, заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (Л)"</string>
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Используется, заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (П)"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 4286c81..ed0b1df 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -159,7 +159,7 @@
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"منسوخ کریں"</string>
     <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"منسلک ہونے پر جوڑا بنانے سے آپ کے رابطوں اور کال کی سرگزشت تک رسائی حاصل ہو جاتی ہے۔"</string>
     <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> کے ساتھ جوڑا نہیں بنا سکا۔"</string>
-    <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"‏غلط PIN یا پاس کلید کی وجہ سے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> کے ساتھ جوڑا نہیں بنا سکا۔"</string>
+    <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"‏غلط PIN یا پاس کلید کی وجہ سے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> کے ساتھ جوڑا نہیں بنایا جا سکا۔"</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> کے ساتھ مواصلت نہیں ہو سکتی۔"</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> نے جوڑا بنانے کو مسترد کر دیا۔"</string>
     <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"کمپیوٹر"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 56282ec..25ca0c2 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -706,9 +706,9 @@
     <string name="physical_keyboard_title" msgid="4811935435315835220">"实体键盘"</string>
     <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"选择键盘布局"</string>
     <string name="keyboard_layout_default_label" msgid="1997292217218546957">"默认"</string>
-    <string name="turn_screen_on_title" msgid="3266937298097573424">"开启屏幕"</string>
+    <string name="turn_screen_on_title" msgid="3266937298097573424">"唤醒屏幕"</string>
     <string name="allow_turn_screen_on" msgid="6194845766392742639">"允许开启屏幕"</string>
-    <string name="allow_turn_screen_on_description" msgid="43834403291575164">"允许应用开启屏幕。如获授权,该应用便可在您未明确表达意愿的情况下随时开启屏幕。"</string>
+    <string name="allow_turn_screen_on_description" msgid="43834403291575164">"允许应用唤醒屏幕。如获授权,该应用便可在您未明确表达意愿的情况下随时唤醒屏幕。"</string>
     <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"要停止广播“<xliff:g id="APP_NAME">%1$s</xliff:g>”的内容吗?"</string>
     <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"如果广播“<xliff:g id="SWITCHAPP">%1$s</xliff:g>”的内容或更改输出来源,当前的广播就会停止"</string>
     <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"广播“<xliff:g id="SWITCHAPP">%1$s</xliff:g>”的内容"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 0f9517d..363045e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1716,7 +1716,7 @@
     <string name="keyboard_layout_default_label">Default</string>
 
     <!-- Special access > Title for managing turn screen on settings. [CHAR LIMIT=50] -->
-    <string name="turn_screen_on_title">Turn screen on</string>
+    <string name="turn_screen_on_title">Screen turn-on control</string>
     <!-- Label for a setting which controls whether an app can turn the screen on [CHAR LIMIT=45] -->
     <string name="allow_turn_screen_on">Allow turning the screen on</string>
     <!-- Description for a setting which controls whether an app can turn the screen on [CHAR LIMIT=NONE] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
index 57bde56..c365142 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
@@ -128,7 +128,7 @@
         final long now = mClock.millis();
         final UserManager um = mContext.getSystemService(UserManager.class);
         final List<UserHandle> profiles = um.getUserProfiles();
-        ArrayMap<UserHandle, Boolean> shouldIncludeAppsByUsers = new ArrayMap<>();
+        ArrayMap<UserHandle, Boolean> shouldHideAppsByUsers = new ArrayMap<>();
 
         for (int i = 0; i < appOpsCount; ++i) {
             AppOpsManager.PackageOps ops = appOps.get(i);
@@ -136,13 +136,13 @@
             int uid = ops.getUid();
             UserHandle user = UserHandle.getUserHandleForUid(uid);
 
-            if (!shouldIncludeAppsByUsers.containsKey(user)) {
-                shouldIncludeAppsByUsers.put(user, shouldHideUser(um, user));
+            if (!shouldHideAppsByUsers.containsKey(user)) {
+                shouldHideAppsByUsers.put(user, shouldHideUser(um, user));
             }
 
             // Don't show apps belonging to background users except for profiles that shouldn't
             // be shown in quiet mode.
-            if (!profiles.contains(user) || !shouldIncludeAppsByUsers.get(user)) {
+            if (!profiles.contains(user) || shouldHideAppsByUsers.get(user)) {
                 continue;
             }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index 822a608..1040ac6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -36,6 +36,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.telephony.SmsApplication;
@@ -56,13 +57,22 @@
 
     private static PowerAllowlistBackend sInstance;
 
+    private final Object mAllowlistedAppsLock = new Object();
+    private final Object mSysAllowlistedAppsLock = new Object();
+    private final Object mDefaultActiveAppsLock = new Object();
+
     private final Context mAppContext;
     private final IDeviceIdleController mDeviceIdleService;
+
+    @GuardedBy("mAllowlistedAppsLock")
     private final ArraySet<String> mAllowlistedApps = new ArraySet<>();
+    @GuardedBy("mSysAllowlistedAppsLock")
     private final ArraySet<String> mSysAllowlistedApps = new ArraySet<>();
+    @GuardedBy("mDefaultActiveAppsLock")
     private final ArraySet<String> mDefaultActiveApps = new ArraySet<>();
 
-    public PowerAllowlistBackend(Context context) {
+    @VisibleForTesting
+    PowerAllowlistBackend(Context context) {
         this(context, IDeviceIdleController.Stub.asInterface(
                 ServiceManager.getService(DEVICE_IDLE_SERVICE)));
     }
@@ -75,24 +85,25 @@
     }
 
     public int getAllowlistSize() {
-        return mAllowlistedApps.size();
-    }
-
-    /**
-    * Check if target package is in System allow list
-    */
-    public boolean isSysAllowlisted(String pkg) {
-        return mSysAllowlistedApps.contains(pkg);
-    }
-
-    /**
-     * Check if target package is in allow list
-     */
-    public boolean isAllowlisted(String pkg, int uid) {
-        if (mAllowlistedApps.contains(pkg)) {
-            return true;
+        synchronized (mAllowlistedAppsLock) {
+            return mAllowlistedApps.size();
         }
+    }
 
+    /** Check if target package is in System allow list */
+    public boolean isSysAllowlisted(String pkg) {
+        synchronized (mSysAllowlistedAppsLock) {
+            return mSysAllowlistedApps.contains(pkg);
+        }
+    }
+
+    /** Check if target package is in allow list */
+    public boolean isAllowlisted(String pkg, int uid) {
+        synchronized (mAllowlistedAppsLock) {
+            if (mAllowlistedApps.contains(pkg)) {
+                return true;
+            }
+        }
         if (isDefaultActiveApp(pkg, uid)) {
             return true;
         }
@@ -100,16 +111,16 @@
         return false;
     }
 
-    /**
-     * Check if it is default active app in multiple area(i.e. SMS, Dialer, Device admin..)
-     */
+    /** Check if it is default active app in multiple area */
     public boolean isDefaultActiveApp(String pkg, int uid) {
         // Additionally, check if pkg is default dialer/sms. They are considered essential apps and
         // should be automatically allowlisted (otherwise user may be able to set restriction on
         // them, leading to bad device behavior.)
 
-        if (mDefaultActiveApps.contains(pkg)) {
-            return true;
+        synchronized (mDefaultActiveAppsLock) {
+            if (mDefaultActiveApps.contains(pkg)) {
+                return true;
+            }
         }
 
         final DevicePolicyManager devicePolicyManager = mAppContext.getSystemService(
@@ -143,9 +154,7 @@
                 DEFAULT_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED);
     }
 
-    /**
-     * Check if target package is in allow list except idle app
-     */
+    /** Check if target package is in allow list except idle app */
     public boolean isAllowlistedExceptIdle(String pkg) {
         try {
             return mDeviceIdleService.isPowerSaveWhitelistExceptIdleApp(pkg);
@@ -156,6 +165,7 @@
     }
 
     /**
+     * Check if target package is in allow list except idle app
      *
      * @param pkgs a list of packageName
      * @return true when one of package is in allow list
@@ -174,20 +184,21 @@
     }
 
     /**
-     * Add app into power save allow list.
+     * Add app into power save allow list
+     *
      * @param pkg packageName of the app
      */
-    // TODO: Fix all callers to pass in UID
     public void addApp(String pkg) {
         addApp(pkg, Process.INVALID_UID);
     }
 
     /**
-     * Add app into power save allow list.
+     * Add app into power save allow list
+     *
      * @param pkg packageName of the app
      * @param uid uid of the app
      */
-    public void addApp(String pkg, int uid) {
+    public synchronized void addApp(String pkg, int uid) {
         try {
             if (android.app.Flags.appRestrictionsApi()) {
                 if (uid == Process.INVALID_UID) {
@@ -204,7 +215,9 @@
             }
 
             mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
-            mAllowlistedApps.add(pkg);
+            synchronized (mAllowlistedAppsLock) {
+                mAllowlistedApps.add(pkg);
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
         } catch (NameNotFoundException e) {
@@ -213,7 +226,8 @@
     }
 
     /**
-     * Remove package from power save allow list.
+     * Remove package from power save allow list
+     *
      * @param pkg packageName of the app
      */
     public void removeApp(String pkg) {
@@ -222,10 +236,11 @@
 
     /**
      * Remove package from power save allow list.
+     *
      * @param pkg packageName of the app
      * @param uid uid of the app
      */
-    public void removeApp(String pkg, int uid) {
+    public synchronized void removeApp(String pkg, int uid) {
         try {
             if (android.app.Flags.appRestrictionsApi()) {
                 if (uid == Process.INVALID_UID) {
@@ -241,7 +256,9 @@
             }
 
             mDeviceIdleService.removePowerSaveWhitelistApp(pkg);
-            mAllowlistedApps.remove(pkg);
+            synchronized (mAllowlistedAppsLock) {
+                mAllowlistedApps.remove(pkg);
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
         } catch (NameNotFoundException e) {
@@ -249,25 +266,33 @@
         }
     }
 
-    /**
-     * Refresh all of lists
-     */
+    /** Refresh all of lists */
     @VisibleForTesting
-    public void refreshList() {
-        mSysAllowlistedApps.clear();
-        mAllowlistedApps.clear();
-        mDefaultActiveApps.clear();
+    public synchronized void refreshList() {
+        synchronized (mSysAllowlistedAppsLock) {
+            mSysAllowlistedApps.clear();
+        }
+        synchronized (mAllowlistedAppsLock) {
+            mAllowlistedApps.clear();
+        }
+        synchronized (mDefaultActiveAppsLock) {
+            mDefaultActiveApps.clear();
+        }
         if (mDeviceIdleService == null) {
             return;
         }
         try {
             final String[] allowlistedApps = mDeviceIdleService.getFullPowerWhitelist();
-            for (String app : allowlistedApps) {
-                mAllowlistedApps.add(app);
+            synchronized (mAllowlistedAppsLock) {
+                for (String app : allowlistedApps) {
+                    mAllowlistedApps.add(app);
+                }
             }
             final String[] sysAllowlistedApps = mDeviceIdleService.getSystemPowerWhitelist();
-            for (String app : sysAllowlistedApps) {
-                mSysAllowlistedApps.add(app);
+            synchronized (mSysAllowlistedAppsLock) {
+                for (String app : sysAllowlistedApps) {
+                    mSysAllowlistedApps.add(app);
+                }
             }
             final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TELEPHONY);
@@ -278,26 +303,28 @@
 
             if (hasTelephony) {
                 if (defaultSms != null) {
-                    mDefaultActiveApps.add(defaultSms.getPackageName());
+                    synchronized (mDefaultActiveAppsLock) {
+                        mDefaultActiveApps.add(defaultSms.getPackageName());
+                    }
                 }
                 if (!TextUtils.isEmpty(defaultDialer)) {
-                    mDefaultActiveApps.add(defaultDialer);
+                    synchronized (mDefaultActiveAppsLock) {
+                        mDefaultActiveApps.add(defaultDialer);
+                    }
                 }
             }
-        } catch (RemoteException e) {
-            Log.w(TAG, "Unable to reach IDeviceIdleController", e);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to invoke refreshList()", e);
         }
     }
 
-    /**
-     * @param context
-     * @return a PowerAllowlistBackend object
-     */
+    /** Get the {@link PowerAllowlistBackend} instance */
     public static PowerAllowlistBackend getInstance(Context context) {
-        if (sInstance == null) {
-            sInstance = new PowerAllowlistBackend(context);
+        synchronized (PowerAllowlistBackend.class) {
+            if (sInstance == null) {
+                sInstance = new PowerAllowlistBackend(context);
+            }
+            return sInstance;
         }
-        return sInstance;
     }
-
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt b/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt
new file mode 100644
index 0000000..d69c87b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.satellite
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.OutcomeReceiver
+import android.telephony.satellite.SatelliteManager
+import android.util.Log
+import android.view.WindowManager
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import com.android.settingslib.wifi.WifiUtils
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Dispatchers.Default
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.TimeoutException
+import kotlin.coroutines.resume
+
+/** A util for Satellite dialog */
+object SatelliteDialogUtils {
+
+    /**
+     * Uses to start Satellite dialog to prevent users from using the BT, Airplane Mode, and
+     * Wifi during the satellite mode is on.
+     */
+    @JvmStatic
+    fun mayStartSatelliteWarningDialog(
+            context: Context,
+            lifecycleOwner: LifecycleOwner,
+            type: Int,
+            allowClick: (isAllowed: Boolean) -> Unit
+    ): Job {
+        return mayStartSatelliteWarningDialog(
+                context, lifecycleOwner.lifecycleScope, type, allowClick)
+    }
+
+    /**
+     * Uses to start Satellite dialog to prevent users from using the BT, Airplane Mode, and
+     * Wifi during the satellite mode is on.
+     */
+    @JvmStatic
+    fun mayStartSatelliteWarningDialog(
+            context: Context,
+            coroutineScope: CoroutineScope,
+            type: Int,
+            allowClick: (isAllowed: Boolean) -> Unit
+    ): Job =
+            coroutineScope.launch {
+                var isSatelliteModeOn = false
+                try {
+                    isSatelliteModeOn = requestIsEnabled(context)
+                } catch (e: InterruptedException) {
+                    Log.w(TAG, "Error to get satellite status : $e")
+                } catch (e: ExecutionException) {
+                    Log.w(TAG, "Error to get satellite status : $e")
+                } catch (e: TimeoutException) {
+                    Log.w(TAG, "Error to get satellite status : $e")
+                }
+
+                if (isSatelliteModeOn) {
+                    startSatelliteWarningDialog(context, type)
+                }
+                withContext(Dispatchers.Main) {
+                    allowClick(!isSatelliteModeOn)
+                }
+            }
+
+    private fun startSatelliteWarningDialog(context: Context, type: Int) {
+        context.startActivity(Intent(Intent.ACTION_MAIN).apply {
+            component = ComponentName(
+                    "com.android.settings",
+                    "com.android.settings.network.SatelliteWarningDialogActivity"
+            )
+            putExtra(WifiUtils.DIALOG_WINDOW_TYPE,
+                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+            putExtra(EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG, type)
+            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
+        })
+    }
+
+    /**
+     * Checks if the satellite modem is enabled.
+     *
+     * @param executor The executor to run the asynchronous operation on
+     * @return A ListenableFuture that will resolve to `true` if the satellite modem enabled,
+     *         `false` otherwise.
+     */
+    private suspend fun requestIsEnabled(
+            context: Context,
+    ): Boolean = withContext(Default) {
+        val satelliteManager: SatelliteManager? =
+                context.getSystemService(SatelliteManager::class.java)
+        if (satelliteManager == null) {
+            Log.w(TAG, "SatelliteManager is null")
+            return@withContext false
+        }
+
+        suspendCancellableCoroutine {continuation ->
+            satelliteManager?.requestIsEnabled(Default.asExecutor(),
+                    object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
+                        override fun onResult(result: Boolean) {
+                            Log.i(TAG, "Satellite modem enabled status: $result")
+                            continuation.resume(result)
+                        }
+
+                        override fun onError(error: SatelliteManager.SatelliteException) {
+                            super.onError(error)
+                            Log.w(TAG, "Can't get satellite modem enabled status", error)
+                            continuation.resume(false)
+                        }
+                    })
+        }
+    }
+
+    const val TAG = "SatelliteDialogUtils"
+
+    const val EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG: String =
+            "extra_type_of_satellite_warning_dialog"
+    const val TYPE_IS_UNKNOWN = -1
+    const val TYPE_IS_WIFI = 0
+    const val TYPE_IS_BLUETOOTH = 1
+    const val TYPE_IS_AIRPLANE_MODE = 2
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
index 2a44511..a939ed1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
@@ -17,6 +17,7 @@
 package com.android.settingslib.statusbar.notification.data.repository
 
 import android.app.NotificationManager
+import android.provider.Settings
 import com.android.settingslib.statusbar.notification.data.model.ZenMode
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -28,10 +29,14 @@
     override val notificationPolicy: StateFlow<NotificationManager.Policy?>
         get() = mutableNotificationPolicy.asStateFlow()
 
-    private val mutableZenMode = MutableStateFlow<ZenMode?>(null)
+    private val mutableZenMode = MutableStateFlow<ZenMode?>(ZenMode(Settings.Global.ZEN_MODE_OFF))
     override val zenMode: StateFlow<ZenMode?>
         get() = mutableZenMode.asStateFlow()
 
+    init {
+        updateNotificationPolicy()
+    }
+
     fun updateNotificationPolicy(policy: NotificationManager.Policy?) {
         mutableNotificationPolicy.value = policy
     }
@@ -48,13 +53,14 @@
     suppressedVisualEffects: Int = NotificationManager.Policy.SUPPRESSED_EFFECTS_UNSET,
     state: Int = NotificationManager.Policy.STATE_UNSET,
     priorityConversationSenders: Int = NotificationManager.Policy.CONVERSATION_SENDERS_NONE,
-) = updateNotificationPolicy(
-    NotificationManager.Policy(
-        priorityCategories,
-        priorityCallSenders,
-        priorityMessageSenders,
-        suppressedVisualEffects,
-        state,
-        priorityConversationSenders,
+) =
+    updateNotificationPolicy(
+        NotificationManager.Policy(
+            priorityCategories,
+            priorityCallSenders,
+            priorityMessageSenders,
+            suppressedVisualEffects,
+            state,
+            priorityConversationSenders,
+        )
     )
-)
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index 65a5317..36e396fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -72,7 +72,11 @@
 
     suspend fun setVolume(audioStream: AudioStream, volume: Int)
 
-    suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean)
+    /**
+     * Mutes and un-mutes [audioStream]. Returns true when the state changes and false the
+     * otherwise.
+     */
+    suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean
 
     suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode)
 
@@ -164,14 +168,20 @@
             audioManager.setStreamVolume(audioStream.value, volume, 0)
         }
 
-    override suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) =
-        withContext(backgroundCoroutineContext) {
-            audioManager.adjustStreamVolume(
-                audioStream.value,
-                if (isMuted) AudioManager.ADJUST_MUTE else AudioManager.ADJUST_UNMUTE,
-                0,
-            )
+    override suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean {
+        return withContext(backgroundCoroutineContext) {
+            if (isMuted == audioManager.isStreamMute(audioStream.value)) {
+                false
+            } else {
+                audioManager.adjustStreamVolume(
+                    audioStream.value,
+                    if (isMuted) AudioManager.ADJUST_MUTE else AudioManager.ADJUST_UNMUTE,
+                    0,
+                )
+                true
+            }
         }
+    }
 
     override suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) {
         withContext(backgroundCoroutineContext) { audioManager.ringerMode = mode.value }
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt
deleted file mode 100644
index 1f037c0..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.volume.data.repository
-
-import android.media.MediaMetadata
-import android.media.session.MediaController
-import android.media.session.MediaSession
-import android.media.session.PlaybackState
-import android.os.Bundle
-import android.os.Handler
-import kotlinx.coroutines.channels.ProducerScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.launch
-
-/** [MediaController.Callback] flow representation. */
-fun MediaController.stateChanges(handler: Handler): Flow<MediaControllerChange> {
-    return callbackFlow {
-        val callback = MediaControllerCallbackProducer(this)
-        registerCallback(callback, handler)
-        awaitClose { unregisterCallback(callback) }
-    }
-}
-
-/** Models particular change event received by [MediaController.Callback]. */
-sealed interface MediaControllerChange {
-
-    data object SessionDestroyed : MediaControllerChange
-
-    data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChange
-
-    data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChange
-
-    data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChange
-
-    data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) :
-        MediaControllerChange
-
-    data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChange
-
-    data class ExtrasChanged(val extras: Bundle?) : MediaControllerChange
-
-    data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) : MediaControllerChange
-}
-
-private class MediaControllerCallbackProducer(
-    private val producingScope: ProducerScope<MediaControllerChange>
-) : MediaController.Callback() {
-
-    override fun onSessionDestroyed() {
-        send(MediaControllerChange.SessionDestroyed)
-    }
-
-    override fun onSessionEvent(event: String, extras: Bundle?) {
-        send(MediaControllerChange.SessionEvent(event, extras))
-    }
-
-    override fun onPlaybackStateChanged(state: PlaybackState?) {
-        send(MediaControllerChange.PlaybackStateChanged(state))
-    }
-
-    override fun onMetadataChanged(metadata: MediaMetadata?) {
-        send(MediaControllerChange.MetadataChanged(metadata))
-    }
-
-    override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) {
-        send(MediaControllerChange.QueueChanged(queue))
-    }
-
-    override fun onQueueTitleChanged(title: CharSequence?) {
-        send(MediaControllerChange.QueueTitleChanged(title))
-    }
-
-    override fun onExtrasChanged(extras: Bundle?) {
-        send(MediaControllerChange.ExtrasChanged(extras))
-    }
-
-    override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
-        send(MediaControllerChange.AudioInfoChanged(info))
-    }
-
-    private fun send(change: MediaControllerChange) {
-        producingScope.launch { producingScope.send(change) }
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
index 33f917e..0e5ebda 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
@@ -25,6 +25,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
 
 /** Provides audio stream state and an ability to change it */
@@ -46,8 +47,16 @@
     val ringerMode: StateFlow<RingerMode>
         get() = audioRepository.ringerMode
 
-    suspend fun setVolume(audioStream: AudioStream, volume: Int) =
+    suspend fun setVolume(audioStream: AudioStream, volume: Int) {
+        val streamModel = getAudioStream(audioStream).first()
+        val oldVolume = streamModel.volume
         audioRepository.setVolume(audioStream, volume)
+        when {
+            volume == streamModel.minVolume -> setMuted(audioStream, true)
+            oldVolume == streamModel.minVolume && volume > streamModel.minVolume ->
+                setMuted(audioStream, false)
+        }
+    }
 
     suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) {
         if (audioStream.value == AudioManager.STREAM_RING) {
@@ -55,7 +64,16 @@
                 if (isMuted) AudioManager.RINGER_MODE_VIBRATE else AudioManager.RINGER_MODE_NORMAL
             audioRepository.setRingerMode(audioStream, RingerMode(mode))
         }
-        audioRepository.setMuted(audioStream, isMuted)
+        val mutedChanged = audioRepository.setMuted(audioStream, isMuted)
+        if (mutedChanged && !isMuted) {
+            with(getAudioStream(audioStream).first()) {
+                if (volume == minVolume) {
+                    // Slightly increase volume when user un-mutes the stream that is lowered
+                    // down to its minimum
+                    setVolume(audioStream, volume + 1)
+                }
+            }
+        }
     }
 
     /** Checks if the volume can be changed via the UI. */
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index f4ddd0a..e125083 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -41,7 +41,10 @@
 //###########################################################
 android_robolectric_test {
     name: "SettingsLibRoboTests",
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
     static_libs: [
         "Settings_robolectric_meta_service_file",
         "Robolectric_shadows_androidx_fragment_upstream",
@@ -51,6 +54,7 @@
         "androidx.core_core",
         "flag-junit",
         "settingslib_media_flags_lib",
+        "settingslib_illustrationpreference_flags_lib",
         "testng", // TODO: remove once JUnit on Android provides assertThrows
     ],
     java_resource_dirs: ["config"],
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt
new file mode 100644
index 0000000..aeda1ed6
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.satellite
+
+import android.content.Context
+import android.content.Intent
+import android.os.OutcomeReceiver
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.telephony.satellite.SatelliteManager
+import android.telephony.satellite.SatelliteManager.SatelliteException
+import android.util.AndroidRuntimeException
+import androidx.test.core.app.ApplicationProvider
+import com.android.internal.telephony.flags.Flags
+import com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.`when`
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.verify
+import org.mockito.internal.verification.Times
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class SatelliteDialogUtilsTest {
+    @JvmField
+    @Rule
+    val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    @Spy
+    var context: Context = ApplicationProvider.getApplicationContext()
+    @Mock
+    private lateinit var satelliteManager: SatelliteManager
+
+    private val coroutineScope = CoroutineScope(Dispatchers.Main)
+
+    @Before
+    fun setUp() {
+        `when`(context.getSystemService(SatelliteManager::class.java))
+                .thenReturn(satelliteManager)
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun mayStartSatelliteWarningDialog_satelliteIsOn_showWarningDialog() = runBlocking {
+        `when`(
+                satelliteManager.requestIsEnabled(
+                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
+                )
+        )
+                .thenAnswer { invocation ->
+                    val receiver = invocation
+                            .getArgument<
+                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
+                                    1
+                            )
+                    receiver.onResult(true)
+                    null
+                }
+
+        try {
+            SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+                    context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+                        assertTrue(it)
+                })
+        } catch (e: AndroidRuntimeException) {
+            // Catch exception of starting activity .
+        }
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun mayStartSatelliteWarningDialog_satelliteIsOff_notShowWarningDialog() = runBlocking {
+        `when`(
+                satelliteManager.requestIsEnabled(
+                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
+                )
+        )
+                .thenAnswer { invocation ->
+                    val receiver = invocation
+                            .getArgument<
+                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
+                                    1
+                            )
+                    receiver.onResult(false)
+                    null
+                }
+
+
+        SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+                assertFalse(it)
+            })
+
+        verify(context, Times(0)).startActivity(any<Intent>())
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun mayStartSatelliteWarningDialog_noSatelliteManager_notShowWarningDialog() = runBlocking {
+        `when`(context.getSystemService(SatelliteManager::class.java))
+                .thenReturn(null)
+
+        SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+                assertFalse(it)
+            })
+
+        verify(context, Times(0)).startActivity(any<Intent>())
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun mayStartSatelliteWarningDialog_satelliteErrorResult_notShowWarningDialog() = runBlocking {
+        `when`(
+                satelliteManager.requestIsEnabled(
+                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
+                )
+        )
+                .thenAnswer { invocation ->
+                    val receiver = invocation
+                            .getArgument<
+                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
+                                    1
+                            )
+                    receiver.onError(SatelliteException(SatelliteManager.SATELLITE_RESULT_ERROR))
+                    null
+                }
+
+
+        SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+                assertFalse(it)
+            })
+
+        verify(context, Times(0)).startActivity(any<Intent>())
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
index 6590bbd..ca53fc2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -26,10 +26,14 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.drawable.AnimatedImageDrawable;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.AnimationDrawable;
 import android.net.Uri;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -39,11 +43,13 @@
 import androidx.preference.PreferenceViewHolder;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settingslib.widget.flags.Flags;
 import com.android.settingslib.widget.preference.illustration.R;
 
 import com.airbnb.lottie.LottieAnimationView;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -51,10 +57,14 @@
 import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
 
+import java.io.ByteArrayInputStream;
+
 
 @RunWith(RobolectricTestRunner.class)
 public class IllustrationPreferenceTest {
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Mock
     private ViewGroup mRootView;
     private Uri mImageUri;
@@ -66,6 +76,7 @@
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private IllustrationPreference.OnBindListener mOnBindListener;
     private LottieAnimationView mOnBindListenerAnimationView;
+    private FrameLayout mIllustrationFrame;
 
     @Before
     public void setUp() {
@@ -75,14 +86,14 @@
         mBackgroundView = new ImageView(mContext);
         mAnimationView = spy(new LottieAnimationView(mContext));
         mMiddleGroundLayout = new FrameLayout(mContext);
-        final FrameLayout illustrationFrame = new FrameLayout(mContext);
-        illustrationFrame.setLayoutParams(
+        mIllustrationFrame = new FrameLayout(mContext);
+        mIllustrationFrame.setLayoutParams(
                 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                         ViewGroup.LayoutParams.WRAP_CONTENT));
         doReturn(mMiddleGroundLayout).when(mRootView).findViewById(R.id.middleground_layout);
         doReturn(mBackgroundView).when(mRootView).findViewById(R.id.background_view);
         doReturn(mAnimationView).when(mRootView).findViewById(R.id.lottie_view);
-        doReturn(illustrationFrame).when(mRootView).findViewById(R.id.illustration_frame);
+        doReturn(mIllustrationFrame).when(mRootView).findViewById(R.id.illustration_frame);
         mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(mRootView));
 
         final AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
@@ -158,11 +169,13 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
     public void playLottieAnimationWithResource_verifyFailureListener() {
         // fake the valid lottie image
         final int fakeValidResId = 111;
         doNothing().when(mAnimationView).setImageResource(fakeValidResId);
         doReturn(null).when(mAnimationView).getDrawable();
+        doNothing().when(mAnimationView).setAnimation(fakeValidResId);
 
         mPreference.setLottieAnimationResId(fakeValidResId);
         mPreference.onBindViewHolder(mViewHolder);
@@ -171,6 +184,50 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+    public void handleImageWithAnimation_emptyInputStreamDisabledFlag_verifyContainerVisible() {
+        doNothing().when(mAnimationView).setImageResource(111);
+        doReturn(null).when(mAnimationView).getDrawable();
+
+        mPreference.setLottieAnimationResId(111);
+        mPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mAnimationView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+    public void handleImageWithAnimation_emptyInputStreamEnabledFlag_verifyContainerHidden() {
+        Resources res = spy(mContext.getResources());
+        doReturn(res).when(mAnimationView).getResources();
+        doReturn(new ByteArrayInputStream(new byte[] {})).when(res).openRawResource(111);
+
+        mPreference.setLottieAnimationResId(111);
+        mPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mAnimationView.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+    public void handleImageWithAnimation_nonEmptyInputStreamEnabledFlag_verifyContainerVisible() {
+        Resources res = spy(mContext.getResources());
+        doReturn(res).when(mAnimationView).getResources();
+        doReturn(new ByteArrayInputStream(new byte[] { 1, 2, 3 })).when(res).openRawResource(111);
+        doNothing().when(mAnimationView).setImageResource(111);
+        doNothing().when(mAnimationView).setAnimation(111);
+        doReturn(null).when(mAnimationView).getDrawable();
+
+        mPreference.setLottieAnimationResId(111);
+        mPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mAnimationView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
     public void setMaxHeight_smallerThanRestrictedHeight_matchResult() {
         final int restrictedHeight =
                 mContext.getResources().getDimensionPixelSize(
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index be3f410..888e395 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -274,6 +274,9 @@
         Settings.Secure.SCREEN_RESOLUTION_MODE,
         Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS,
         Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
-        Settings.Secure.CHARGE_OPTIMIZATION_MODE
+        Settings.Secure.CHARGE_OPTIMIZATION_MODE,
+        Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
+        Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
+        Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index b1feede..b992ddc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -18,6 +18,7 @@
 
 import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_LONG_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.AUTOFILL_SERVICE_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
@@ -433,5 +434,8 @@
         VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
                 new InclusiveIntegerRangeValidator(0, 10));
         VALIDATORS.put(Secure.CHARGE_OPTIMIZATION_MODE, new InclusiveIntegerRangeValidator(0, 10));
+        VALIDATORS.put(Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR);
+        VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR);
+        VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, NONE_NEGATIVE_LONG_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index 677c81a..255b1ad 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -239,6 +239,18 @@
         }
     };
 
+    static final Validator ANY_LONG_VALIDATOR = value -> {
+        if (value == null) {
+            return true;
+        }
+        try {
+            Long.parseLong(value);
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    };
+
     static final Validator CREDENTIAL_SERVICE_VALIDATOR = new Validator() {
         @Override
         public boolean validate(String value) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 92167ee..ab9a30b 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -233,6 +233,7 @@
                     Settings.Global.ENHANCED_4G_MODE_ENABLED,
                     Settings.Global.ENABLE_16K_PAGES, // Added for 16K developer option
                     Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
+                    Settings.Global.ERROR_KERNEL_LOG_PREFIX,
                     Settings.Global.ERROR_LOGCAT_PREFIX,
                     Settings.Global.EUICC_PROVISIONED,
                     Settings.Global.EUICC_SUPPORTED_COUNTRIES,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 46bf494..374240b 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -347,6 +347,7 @@
     <uses-permission android:name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
     <uses-permission android:name="android.permission.USE_COMPANION_TRANSPORTS" />
     <uses-permission android:name="android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE" />
+    <uses-permission android:name="android.permission.DELIVER_COMPANION_MESSAGES" />
 
     <uses-permission android:name="android.permission.MANAGE_APPOPS" />
     <uses-permission android:name="android.permission.WATCH_APPOPS" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c04ec4f..d2ca112 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -78,11 +78,23 @@
     visibility: ["//visibility:private"],
 }
 
+filegroup {
+    name: "SystemUI-tests-broken-robofiles-run",
+    srcs: [
+        "tests/src/**/systemui/util/LifecycleFragmentTest.java",
+        "tests/src/**/systemui/util/TestableAlertDialogTest.kt",
+        "tests/src/**/systemui/util/kotlin/PairwiseFlowTest",
+        "tests/src/**/systemui/util/sensors/AsyncManagerTest.java",
+        "tests/src/**/systemui/util/sensors/ThresholdSensorImplTest.java",
+        "tests/src/**/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java",
+    ],
+}
+
 // We are running robolectric tests in the tests directory as well as
 // multivalent tests.  If you add a test, and it doesn't run in robolectric,
 // it should be added to this exclusion list. go/multivalent-tests
 filegroup {
-    name: "SystemUI-tests-broken-robofiles",
+    name: "SystemUI-tests-broken-robofiles-compile",
     srcs: [
         "tests/src/**/*DeviceOnlyTest.java",
         "tests/src/**/*DeviceOnlyTest.kt",
@@ -703,7 +715,8 @@
         ":SystemUI-tests-robofiles",
     ],
     exclude_srcs: [
-        ":SystemUI-tests-broken-robofiles",
+        ":SystemUI-tests-broken-robofiles-compile",
+        ":SystemUI-tests-broken-robofiles-run",
     ],
     static_libs: [
         "RoboTestLibraries",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b9e70ef..9c58371 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -826,20 +826,6 @@
             </intent-filter>
         </activity>
 
-        <activity
-            android:name=".contrast.ContrastDialogActivity"
-            android:label="@string/quick_settings_contrast_label"
-            android:theme="@style/Theme.SystemUI.ContrastDialog"
-            android:finishOnCloseSystemDialogs="true"
-            android:launchMode="singleInstance"
-            android:excludeFromRecents="true"
-            android:exported="true">
-            <intent-filter>
-                <action android:name="com.android.intent.action.SHOW_CONTRAST_DIALOG" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
         <activity android:name=".ForegroundServicesDialog"
             android:process=":fgservices"
             android:excludeFromRecents="true"
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 755fe2a..55edff6 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -4,6 +4,13 @@
 # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
 
 flag {
+    name: "create_windowless_window_magnifier"
+    namespace: "accessibility"
+    description: "Uses SurfaceControlViewHost to create the magnifier for window magnification."
+    bug: "280992417"
+}
+
+flag {
     name: "delay_show_magnification_button"
     namespace: "accessibility"
     description: "Delays the showing of magnification mode switch button."
@@ -66,8 +73,11 @@
 }
 
 flag {
-    name: "create_windowless_window_magnifier"
+    name: "save_and_restore_magnification_settings_buttons"
     namespace: "accessibility"
-    description: "Uses SurfaceControlViewHost to create the magnifier for window magnification."
-    bug: "280992417"
+    description: "Saves the selected button status in magnification settings and restore the status when revisiting the same smallest screen DP."
+    bug: "325567876"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
 }
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index e69ac0a..f9e955b 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -26,11 +26,10 @@
 }
 
 flag {
-
-    name: "notification_heads_up_cycling"
-    namespace: "systemui"
-    description: "Heads-up notification cycling animation for the Notification Avalanche feature."
-    bug: "316404716"
+   name: "priority_people_section"
+   namespace: "systemui"
+   description: "Add a new section for priority people (aka important conversations)."
+   bug: "340294566"
 }
 
 flag {
@@ -197,7 +196,16 @@
     description: "Re-enable the codepath that removed tinting of notifications when the"
         " standard background color is desired.  This was the behavior before we discovered"
         " a resources threading issue, which we worked around by tinting the notification"
-        " backgrounds and footer buttons."
+        " backgrounds."
+    bug: "294830092"
+}
+
+flag {
+    name: "notification_footer_background_tint_optimization"
+    namespace: "systemui"
+    description: "Remove duplicative tinting of notification footer buttons. This was the behavior"
+        " before we discovered a resources threading issue, which we worked around by applying the"
+        " same color as a tint to the background drawable of footer buttons."
     bug: "294830092"
 }
 
@@ -348,6 +356,14 @@
 }
 
 flag {
+    name: "status_bar_screen_sharing_chips"
+    namespace: "systemui"
+    description: "Show chips on the left side of the status bar when a user is screen sharing, "
+        "recording, or casting"
+    bug: "332662551"
+}
+
+flag {
     name: "compose_bouncer"
     namespace: "systemui"
     description: "Use the new compose bouncer in SystemUI"
@@ -398,6 +414,16 @@
 }
 
 flag {
+  name: "fix_image_wallpaper_crash_surface_already_released"
+  namespace: "systemui"
+  description: "Make sure ImageWallpaper doesn't return from OnSurfaceDestroyed until any drawing is finished"
+  bug: "337287154"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
    name: "activity_transition_use_largest_window"
    namespace: "systemui"
    description: "Target largest opening window during activity transitions."
@@ -470,6 +496,26 @@
 }
 
 flag {
+    name: "fix_screenshot_action_dismiss_system_windows"
+    namespace: "systemui"
+    description: "Dismiss existing system windows when starting action from screenshot UI"
+    bug: "309933761"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "screenshot_scroll_crop_view_crash_fix"
+    namespace: "systemui"
+    description: "Mitigate crash on invalid computed range in CropView"
+    bug: "232633995"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "screenshot_private_profile_behavior_fix"
     namespace: "systemui"
     description: "Private profile support for screenshots"
@@ -844,6 +890,9 @@
     namespace: "systemui"
     description: "Enforce BaseUserRestriction for DISALLOW_CONFIG_BRIGHTNESS."
     bug: "329205638"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
 }
 
 flag {
@@ -882,3 +931,40 @@
   description: "Enables Backlinks improvement feature in App Clips"
   bug: "300307759"
 }
+
+flag {
+  name: "qs_custom_tile_click_guaranteed_bug_fix"
+  namespace: "systemui"
+  description: "Guarantee that clicks on a tile always happen by postponing onStopListening until after the click."
+  bug: "339290820"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "media_controls_user_initiated_dismiss"
+  namespace: "systemui"
+  description: "Only dismiss media notifications when the control was removed by the user."
+  bug: "335875159"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "validate_keyboard_shortcut_helper_icon_uri"
+  namespace: "systemui"
+  description: "Adds a check that the caller can access the content URI of an icon in the shortcut helper."
+  bug: "331180422"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "glanceable_hub_gesture_handle"
+  namespace: "systemui"
+  description: "Shows a vertical bar at the right edge to indicate the user can swipe to open the glanceable hub"
+  bug: "339667383"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index d4660fa..23df26f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -23,6 +23,7 @@
 import android.graphics.Matrix
 import android.graphics.Rect
 import android.graphics.RectF
+import android.os.Binder
 import android.os.Build
 import android.os.Handler
 import android.os.Looper
@@ -36,7 +37,11 @@
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_CLOSE
+import android.view.WindowManager.TRANSIT_TO_BACK
 import android.view.animation.PathInterpolator
+import android.window.RemoteTransition
+import android.window.TransitionFilter
 import androidx.annotation.AnyThread
 import androidx.annotation.BinderThread
 import androidx.annotation.UiThread
@@ -44,6 +49,9 @@
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.policy.ScreenDecorationsUtils
 import com.android.systemui.Flags.activityTransitionUseLargestWindow
+import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
+import com.android.wm.shell.shared.IShellTransitions
+import com.android.wm.shell.shared.ShellTransitions
 import java.util.concurrent.Executor
 import kotlin.math.roundToInt
 
@@ -59,6 +67,9 @@
     /** The executor that runs on the main thread. */
     private val mainExecutor: Executor,
 
+    /** The object used to register ephemeral returns and long-lived transitions. */
+    private val transitionRegister: TransitionRegister? = null,
+
     /** The animator used when animating a View into an app. */
     private val transitionAnimator: TransitionAnimator = defaultTransitionAnimator(mainExecutor),
 
@@ -74,6 +85,36 @@
     // TODO(b/301385865): Remove this flag.
     private val disableWmTimeout: Boolean = false,
 ) {
+    @JvmOverloads
+    constructor(
+        mainExecutor: Executor,
+        shellTransitions: ShellTransitions,
+        transitionAnimator: TransitionAnimator = defaultTransitionAnimator(mainExecutor),
+        dialogToAppAnimator: TransitionAnimator = defaultDialogToAppAnimator(mainExecutor),
+        disableWmTimeout: Boolean = false,
+    ) : this(
+        mainExecutor,
+        TransitionRegister.fromShellTransitions(shellTransitions),
+        transitionAnimator,
+        dialogToAppAnimator,
+        disableWmTimeout,
+    )
+
+    @JvmOverloads
+    constructor(
+        mainExecutor: Executor,
+        iShellTransitions: IShellTransitions,
+        transitionAnimator: TransitionAnimator = defaultTransitionAnimator(mainExecutor),
+        dialogToAppAnimator: TransitionAnimator = defaultDialogToAppAnimator(mainExecutor),
+        disableWmTimeout: Boolean = false,
+    ) : this(
+        mainExecutor,
+        TransitionRegister.fromIShellTransitions(iShellTransitions),
+        transitionAnimator,
+        dialogToAppAnimator,
+        disableWmTimeout,
+    )
+
     companion object {
         /** The timings when animating a View into an app. */
         @JvmField
@@ -233,6 +274,10 @@
             }
         }
 
+        if (animationAdapter != null && controller.transitionCookie != null) {
+            registerEphemeralReturnAnimation(controller, transitionRegister)
+        }
+
         val launchResult = intentStarter(animationAdapter)
 
         // Only animate if the app is not already on top and will be opened, unless we are on the
@@ -302,6 +347,66 @@
         }
     }
 
+    /**
+     * Uses [transitionRegister] to set up the return animation for the given [launchController].
+     *
+     * De-registration is set up automatically once the return animation is run.
+     *
+     * TODO(b/339194555): automatically de-register when the launchable is detached.
+     */
+    private fun registerEphemeralReturnAnimation(
+        launchController: Controller,
+        transitionRegister: TransitionRegister?
+    ) {
+        if (!returnAnimationFrameworkLibrary()) return
+
+        var cleanUpRunnable: Runnable? = null
+        val returnRunner =
+            createRunner(
+                object : DelegateTransitionAnimatorController(launchController) {
+                    override val isLaunching = false
+
+                    override fun onTransitionAnimationCancelled(
+                        newKeyguardOccludedState: Boolean?
+                    ) {
+                        super.onTransitionAnimationCancelled(newKeyguardOccludedState)
+                        cleanUp()
+                    }
+
+                    override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
+                        super.onTransitionAnimationEnd(isExpandingFullyAbove)
+                        cleanUp()
+                    }
+
+                    private fun cleanUp() {
+                        cleanUpRunnable?.run()
+                    }
+                }
+            )
+
+        // mTypeSet and mModes match back signals only, and not home. This is on purpose, because
+        // we only want ephemeral return animations triggered in these scenarios.
+        val filter =
+            TransitionFilter().apply {
+                mTypeSet = intArrayOf(TRANSIT_CLOSE, TRANSIT_TO_BACK)
+                mRequirements =
+                    arrayOf(
+                        TransitionFilter.Requirement().apply {
+                            mLaunchCookie = launchController.transitionCookie
+                            mModes = intArrayOf(TRANSIT_CLOSE, TRANSIT_TO_BACK)
+                        }
+                    )
+            }
+        val transition =
+            RemoteTransition(
+                RemoteAnimationRunnerCompat.wrap(returnRunner),
+                "${launchController.transitionCookie}_returnTransition"
+            )
+
+        transitionRegister?.register(filter, transition)
+        cleanUpRunnable = Runnable { transitionRegister?.unregister(transition) }
+    }
+
     /** Add a [Listener] that can listen to transition animations. */
     fun addListener(listener: Listener) {
         listeners.add(listener)
@@ -386,8 +491,14 @@
              * Note: The background of [view] should be a (rounded) rectangle so that it can be
              * properly animated.
              */
+            @JvmOverloads
             @JvmStatic
-            fun fromView(view: View, cujType: Int? = null): Controller? {
+            fun fromView(
+                view: View,
+                cujType: Int? = null,
+                cookie: TransitionCookie? = null,
+                returnCujType: Int? = null
+            ): Controller? {
                 // Make sure the View we launch from implements LaunchableView to avoid visibility
                 // issues.
                 if (view !is LaunchableView) {
@@ -408,7 +519,7 @@
                     return null
                 }
 
-                return GhostedViewTransitionAnimatorController(view, cujType)
+                return GhostedViewTransitionAnimatorController(view, cujType, cookie, returnCujType)
             }
         }
 
@@ -432,6 +543,17 @@
             get() = false
 
         /**
+         * The cookie associated with the transition controlled by this [Controller].
+         *
+         * This should be defined for all return [Controller] (when [isLaunching] is false) and for
+         * their associated launch [Controller]s.
+         *
+         * For the recommended format, see [TransitionCookie].
+         */
+        val transitionCookie: TransitionCookie?
+            get() = null
+
+        /**
          * The intent was started. If [willAnimate] is false, nothing else will happen and the
          * animation will not be started.
          */
@@ -652,7 +774,7 @@
                 return
             }
 
-            val window = findRootTaskIfPossible(apps)
+            val window = findTargetWindowIfPossible(apps)
             if (window == null) {
                 Log.i(TAG, "Aborting the animation as no window is opening")
                 callback?.invoke()
@@ -676,7 +798,7 @@
             startAnimation(window, navigationBar, callback)
         }
 
-        private fun findRootTaskIfPossible(
+        private fun findTargetWindowIfPossible(
             apps: Array<out RemoteAnimationTarget>?
         ): RemoteAnimationTarget? {
             if (apps == null) {
@@ -694,6 +816,19 @@
             for (it in apps) {
                 if (it.mode == targetMode) {
                     if (activityTransitionUseLargestWindow()) {
+                        if (returnAnimationFrameworkLibrary()) {
+                            // If the controller contains a cookie, _only_ match if the candidate
+                            // contains the matching cookie.
+                            if (
+                                controller.transitionCookie != null &&
+                                    it.taskInfo
+                                        ?.launchCookies
+                                        ?.contains(controller.transitionCookie) != true
+                            ) {
+                                continue
+                            }
+                        }
+
                         if (
                             candidate == null ||
                                 !it.hasAnimatingParent && candidate.hasAnimatingParent
@@ -806,11 +941,7 @@
                         progress: Float,
                         linearProgress: Float
                     ) {
-                        // Apply the state to the window only if it is visible, i.e. when the
-                        // expanding view is *not* visible.
-                        if (!state.visible) {
-                            applyStateToWindow(window, state, linearProgress)
-                        }
+                        applyStateToWindow(window, state, linearProgress)
                         navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
 
                         listener?.onTransitionAnimationProgress(linearProgress)
@@ -1048,4 +1179,72 @@
             return (this.width() * this.height()) > (other.width() * other.height())
         }
     }
+
+    /**
+     * Wraps one of the two methods we have to register remote transitions with WM Shell:
+     * - for in-process registrations (e.g. System UI) we use [ShellTransitions]
+     * - for cross-process registrations (e.g. Launcher) we use [IShellTransitions]
+     *
+     * Important: each instance of this class must wrap exactly one of the two.
+     */
+    class TransitionRegister
+    private constructor(
+        private val shellTransitions: ShellTransitions? = null,
+        private val iShellTransitions: IShellTransitions? = null,
+    ) {
+        init {
+            assert((shellTransitions != null).xor(iShellTransitions != null))
+        }
+
+        companion object {
+            /** Provides a [TransitionRegister] instance wrapping [ShellTransitions]. */
+            fun fromShellTransitions(shellTransitions: ShellTransitions): TransitionRegister {
+                return TransitionRegister(shellTransitions = shellTransitions)
+            }
+
+            /** Provides a [TransitionRegister] instance wrapping [IShellTransitions]. */
+            fun fromIShellTransitions(iShellTransitions: IShellTransitions): TransitionRegister {
+                return TransitionRegister(iShellTransitions = iShellTransitions)
+            }
+        }
+
+        /** Register [remoteTransition] with WM Shell using the given [filter]. */
+        internal fun register(
+            filter: TransitionFilter,
+            remoteTransition: RemoteTransition,
+        ) {
+            shellTransitions?.registerRemote(filter, remoteTransition)
+            iShellTransitions?.registerRemote(filter, remoteTransition)
+        }
+
+        /** Unregister [remoteTransition] from WM Shell. */
+        internal fun unregister(remoteTransition: RemoteTransition) {
+            shellTransitions?.unregisterRemote(remoteTransition)
+            iShellTransitions?.unregisterRemote(remoteTransition)
+        }
+    }
+
+    /**
+     * A cookie used to uniquely identify a task launched using an
+     * [ActivityTransitionAnimator.Controller].
+     *
+     * The [String] encapsulated by this class should be formatted in such a way to be unique across
+     * the system, but reliably constant for the same associated launchable.
+     *
+     * Recommended naming scheme:
+     * - DO use the fully qualified name of the class that owns the instance of the launchable,
+     *   along with a concise and precise description of the purpose of the launchable in question.
+     * - DO NOT introduce uniqueness through the use of timestamps or other runtime variables that
+     *   will change if the instance is destroyed and re-created.
+     *
+     * Example: "com.not.the.real.class.name.ShadeController_openSettingsButton"
+     *
+     * Note that sometimes (e.g. in recycler views) there could be multiple instances of the same
+     * launchable, and no static knowledge to adequately differentiate between them using a single
+     * description. In this case, the recommendation is to append a unique identifier related to the
+     * contents of the launchable.
+     *
+     * Example: “com.not.the.real.class.name.ToastWebResult_launchAga_id143256”
+     */
+    data class TransitionCookie(private val cookie: String) : Binder()
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
index e4bb2ad..21557b8 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
@@ -25,10 +25,30 @@
      * [Expandable] into an Activity, or return `null` if this [Expandable] should not be animated
      * (e.g. if it is currently not attached or visible).
      *
-     * @param cujType the CUJ type from the [com.android.internal.jank.InteractionJankMonitor]
+     * @param launchCujType The CUJ type from the [com.android.internal.jank.InteractionJankMonitor]
      *   associated to the launch that will use this controller.
+     * @param cookie The unique cookie associated with the launch that will use this controller.
+     *   This is required iff the a return animation should be included.
+     * @param returnCujType The CUJ type from the [com.android.internal.jank.InteractionJankMonitor]
+     *   associated to the return animation that will use this controller.
      */
-    fun activityTransitionController(cujType: Int? = null): ActivityTransitionAnimator.Controller?
+    fun activityTransitionController(
+        launchCujType: Int? = null,
+        cookie: ActivityTransitionAnimator.TransitionCookie? = null,
+        returnCujType: Int? = null
+    ): ActivityTransitionAnimator.Controller?
+
+    /**
+     * See [activityTransitionController] above.
+     *
+     * Interfaces don't support [JvmOverloads], so this is a useful overload for Java usages that
+     * don't use the return-related parameters.
+     */
+    fun activityTransitionController(
+        launchCujType: Int? = null
+    ): ActivityTransitionAnimator.Controller? {
+        return activityTransitionController(launchCujType, cookie = null, returnCujType = null)
+    }
 
     /**
      * Create a [DialogTransitionAnimator.Controller] that can be used to expand this [Expandable]
@@ -48,9 +68,16 @@
         fun fromView(view: View): Expandable {
             return object : Expandable {
                 override fun activityTransitionController(
-                    cujType: Int?,
+                    launchCujType: Int?,
+                    cookie: ActivityTransitionAnimator.TransitionCookie?,
+                    returnCujType: Int?
                 ): ActivityTransitionAnimator.Controller? {
-                    return ActivityTransitionAnimator.Controller.fromView(view, cujType)
+                    return ActivityTransitionAnimator.Controller.fromView(
+                        view,
+                        launchCujType,
+                        cookie,
+                        returnCujType
+                    )
                 }
 
                 override fun dialogTransitionController(
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
index fd79f62..9d45073 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
@@ -59,8 +59,12 @@
     /** The view that will be ghosted and from which the background will be extracted. */
     private val ghostedView: View,
 
-    /** The [CujType] associated to this animation. */
-    private val cujType: Int? = null,
+    /** The [CujType] associated to this launch animation. */
+    private val launchCujType: Int? = null,
+    override val transitionCookie: ActivityTransitionAnimator.TransitionCookie? = null,
+
+    /** The [CujType] associated to this return animation. */
+    private val returnCujType: Int? = null,
     private var interactionJankMonitor: InteractionJankMonitor =
         InteractionJankMonitor.getInstance(),
 ) : ActivityTransitionAnimator.Controller {
@@ -104,6 +108,15 @@
      */
     private val background: Drawable?
 
+    /** CUJ identifier accounting for whether this controller is for a launch or a return. */
+    private val cujType: Int?
+        get() =
+            if (isLaunching) {
+                launchCujType
+            } else {
+                returnCujType
+            }
+
     init {
         // Make sure the View we launch from implements LaunchableView to avoid visibility issues.
         if (ghostedView !is LaunchableView) {
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CollectAsStateDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CollectAsStateDetector.kt
new file mode 100644
index 0000000..94620c4
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CollectAsStateDetector.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UFile
+import org.jetbrains.uast.UImportStatement
+
+class CollectAsStateDetector : Detector(), SourceCodeScanner {
+
+    override fun getApplicableUastTypes(): List<Class<out UElement>> {
+        return listOf(UFile::class.java)
+    }
+
+    override fun createUastHandler(context: JavaContext): UElementHandler {
+        return object : UElementHandler() {
+            override fun visitFile(node: UFile) {
+                node.imports.forEach { importStatement ->
+                    visitImportStatement(context, importStatement)
+                }
+            }
+        }
+    }
+
+    private fun visitImportStatement(
+        context: JavaContext,
+        importStatement: UImportStatement,
+    ) {
+        val importText = importStatement.importReference?.asSourceString() ?: return
+        if (ILLEGAL_IMPORT == importText) {
+            context.report(
+                issue = ISSUE,
+                scope = importStatement,
+                location = context.getLocation(importStatement),
+                message = "collectAsState considered harmful",
+            )
+        }
+    }
+
+    companion object {
+        @JvmField
+        val ISSUE =
+            Issue.create(
+                id = "OverlyEagerCollectAsState",
+                briefDescription = "collectAsState considered harmful",
+                explanation =
+                    """
+                go/sysui-compose#collect-as-state
+
+                Don't use collectAsState as it will set up a coroutine that keeps collecting from a
+                flow until its coroutine scope becomes inactive. This prevents the work from being
+                properly paused while the surrounding lifecycle becomes paused or stopped and is
+                therefore considered harmful.
+
+                Instead, use Flow.collectAsStateWithLifecycle(initial: T) or
+                StateFlow.collectAsStateWithLifecycle(). These APIs correctly pause the collection
+                coroutine while the lifecycle drops below the specified minActiveState (which
+                defaults to STARTED meaning that it will pause when the Compose-hosting window
+                becomes invisible).
+            """
+                        .trimIndent(),
+                category = Category.PERFORMANCE,
+                priority = 8,
+                severity = Severity.ERROR,
+                implementation =
+                    Implementation(
+                        CollectAsStateDetector::class.java,
+                        Scope.JAVA_FILE_SCOPE,
+                    ),
+            )
+
+        private val ILLEGAL_IMPORT = "androidx.compose.runtime.collectAsState"
+    }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index cecbc47..73ac6cc 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -32,6 +32,7 @@
                 BindServiceOnMainThreadDetector.ISSUE,
                 BroadcastSentViaContextDetector.ISSUE,
                 CleanArchitectureDependencyViolationDetector.ISSUE,
+                CollectAsStateDetector.ISSUE,
                 DumpableNotRegisteredDetector.ISSUE,
                 FlowDetector.SHARED_FLOW_CREATION,
                 SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CollectAsStateDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CollectAsStateDetectorTest.kt
new file mode 100644
index 0000000..6962b4e
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CollectAsStateDetectorTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class CollectAsStateDetectorTest : SystemUILintDetectorTest() {
+
+    override fun getDetector(): Detector {
+        return CollectAsStateDetector()
+    }
+
+    override fun getIssues(): List<Issue> {
+        return listOf(
+            CollectAsStateDetector.ISSUE,
+        )
+    }
+
+    @Test
+    fun testViolation() {
+        lint()
+            .files(COLLECT_AS_STATE_STUB, COLLECT_WITH_LIFECYCLE_AS_STATE_STUB, GOOD_FILE, BAD_FILE)
+            .issues(CollectAsStateDetector.ISSUE)
+            .run()
+            .expect(
+                """
+src/com/android/internal/systemui/lint/Bad.kt:3: Error: collectAsState considered harmful [OverlyEagerCollectAsState]
+import androidx.compose.runtime.collectAsState
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+                """
+                    .trimIndent()
+            )
+    }
+
+    @Test
+    fun testNoViolation() {
+        lint()
+            .files(COLLECT_AS_STATE_STUB, COLLECT_WITH_LIFECYCLE_AS_STATE_STUB, GOOD_FILE)
+            .issues(CollectAsStateDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    companion object {
+        private val COLLECT_AS_STATE_STUB =
+            TestFiles.kotlin(
+                """
+                package androidx.compose.runtime
+
+                fun collectAsState() {}
+            """
+                    .trimIndent()
+            )
+        private val COLLECT_WITH_LIFECYCLE_AS_STATE_STUB =
+            TestFiles.kotlin(
+                """
+                package androidx.lifecycle.compose
+
+                fun collectAsStateWithLifecycle() {}
+            """
+                    .trimIndent()
+            )
+
+        private val BAD_FILE =
+            TestFiles.kotlin(
+                """
+                package com.android.internal.systemui.lint
+
+                import androidx.compose.runtime.collectAsState
+
+                class Bad
+            """
+                    .trimIndent()
+            )
+
+        private val GOOD_FILE =
+            TestFiles.kotlin(
+                """
+                package com.android.internal.systemui.lint
+
+                import androidx.lifecycle.compose.collectAsStateWithLifecycle
+
+                class Good
+            """
+                    .trimIndent()
+            )
+    }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index c7f0a96..17a6061 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -134,13 +134,15 @@
     override val expandable: Expandable =
         object : Expandable {
             override fun activityTransitionController(
-                cujType: Int?,
+                launchCujType: Int?,
+                cookie: ActivityTransitionAnimator.TransitionCookie?,
+                returnCujType: Int?
             ): ActivityTransitionAnimator.Controller? {
                 if (!isComposed.value) {
                     return null
                 }
 
-                return activityController(cujType)
+                return activityController(launchCujType, cookie, returnCujType)
             }
 
             override fun dialogTransitionController(
@@ -262,10 +264,27 @@
     }
 
     /** Create an [ActivityTransitionAnimator.Controller] that can be used to animate activities. */
-    private fun activityController(cujType: Int?): ActivityTransitionAnimator.Controller {
+    private fun activityController(
+        launchCujType: Int?,
+        cookie: ActivityTransitionAnimator.TransitionCookie?,
+        returnCujType: Int?
+    ): ActivityTransitionAnimator.Controller {
         val delegate = transitionController()
         return object :
             ActivityTransitionAnimator.Controller, TransitionAnimator.Controller by delegate {
+            /**
+             * CUJ identifier accounting for whether this controller is for a launch or a return.
+             */
+            private val cujType: Int?
+                get() =
+                    if (isLaunching) {
+                        launchCujType
+                    } else {
+                        returnCujType
+                    }
+
+            override val transitionCookie = cookie
+
             override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
                 delegate.onTransitionAnimationStart(isExpandingFullyAbove)
                 overlay.value = composeViewRoot.rootView.overlay as ViewGroupOverlay
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index c22b50d..fa01a4b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -56,7 +56,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -77,6 +76,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.unit.times
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformButton
 import com.android.compose.animation.Easings
 import com.android.compose.animation.scene.ElementKey
@@ -111,7 +111,7 @@
     dialogFactory: BouncerDialogFactory,
     modifier: Modifier = Modifier,
 ) {
-    val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState()
+    val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsStateWithLifecycle()
     val layout = calculateLayout(isSideBySideSupported = isSideBySideSupported)
 
     Box(
@@ -220,7 +220,7 @@
     viewModel: BouncerViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val authMethod by viewModel.authMethodViewModel.collectAsState()
+    val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle()
 
     Row(
         modifier =
@@ -316,7 +316,7 @@
     val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) }
     val isHeightExpanded =
         LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded
-    val authMethod by viewModel.authMethodViewModel.collectAsState()
+    val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle()
 
     Row(
         modifier =
@@ -480,7 +480,7 @@
     modifier: Modifier = Modifier,
 ) {
     val foldPosture: FoldPosture by foldPosture()
-    val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState()
+    val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsStateWithLifecycle()
     val isSplitAroundTheFold = foldPosture == FoldPosture.Tabletop && isSplitAroundTheFoldRequired
     val currentSceneKey =
         if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
@@ -562,7 +562,7 @@
     viewModel: BouncerMessageViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val message: MessageViewModel? by viewModel.message.collectAsState()
+    val message: MessageViewModel? by viewModel.message.collectAsStateWithLifecycle()
 
     DisposableEffect(Unit) {
         viewModel.onShown()
@@ -612,7 +612,7 @@
     modifier: Modifier = Modifier,
 ) {
     val authMethodViewModel: AuthMethodBouncerViewModel? by
-        viewModel.authMethodViewModel.collectAsState()
+        viewModel.authMethodViewModel.collectAsStateWithLifecycle()
 
     when (val nonNullViewModel = authMethodViewModel) {
         is PinBouncerViewModel ->
@@ -642,7 +642,7 @@
     modifier: Modifier = Modifier,
 ) {
     val authMethodViewModel: AuthMethodBouncerViewModel? by
-        viewModel.authMethodViewModel.collectAsState()
+        viewModel.authMethodViewModel.collectAsStateWithLifecycle()
 
     when (val nonNullViewModel = authMethodViewModel) {
         is PinBouncerViewModel -> {
@@ -668,7 +668,8 @@
     viewModel: BouncerViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
+    val actionButton: BouncerActionButtonModel? by
+        viewModel.actionButton.collectAsStateWithLifecycle()
     val appearFadeInAnimatable = remember { Animatable(0f) }
     val appearMoveAnimatable = remember { Animatable(0f) }
     val appearAnimationInitialOffset = with(LocalDensity.current) { 80.dp.toPx() }
@@ -735,7 +736,7 @@
     bouncerViewModel: BouncerViewModel,
     dialogFactory: BouncerDialogFactory,
 ) {
-    val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsState()
+    val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsStateWithLifecycle()
     var dialog: AlertDialog? by remember { mutableStateOf(null) }
 
     dialogViewModel?.let { viewModel ->
@@ -772,8 +773,8 @@
         return
     }
 
-    val selectedUserImage by viewModel.selectedUserImage.collectAsState(null)
-    val dropdownItems by viewModel.userSwitcherDropdown.collectAsState(emptyList())
+    val selectedUserImage by viewModel.selectedUserImage.collectAsStateWithLifecycle(null)
+    val dropdownItems by viewModel.userSwitcherDropdown.collectAsStateWithLifecycle(emptyList())
 
     Column(
         horizontalAlignment = Alignment.CenterHorizontally,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 2dcd0ff..203bd7a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -27,7 +27,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.ExperimentalComposeUiApi
@@ -49,6 +48,7 @@
 import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformIconButton
 import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
 import com.android.systemui.common.ui.compose.SelectedUserAwareInputConnection
@@ -62,18 +62,20 @@
     modifier: Modifier = Modifier,
 ) {
     val focusRequester = remember { FocusRequester() }
-    val isTextFieldFocusRequested by viewModel.isTextFieldFocusRequested.collectAsState()
+    val isTextFieldFocusRequested by
+        viewModel.isTextFieldFocusRequested.collectAsStateWithLifecycle()
     LaunchedEffect(isTextFieldFocusRequested) {
         if (isTextFieldFocusRequested) {
             focusRequester.requestFocus()
         }
     }
 
-    val password: String by viewModel.password.collectAsState()
-    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
-    val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
-    val isImeSwitcherButtonVisible by viewModel.isImeSwitcherButtonVisible.collectAsState()
-    val selectedUserId by viewModel.selectedUserId.collectAsState()
+    val password: String by viewModel.password.collectAsStateWithLifecycle()
+    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsStateWithLifecycle()
+    val animateFailure: Boolean by viewModel.animateFailure.collectAsStateWithLifecycle()
+    val isImeSwitcherButtonVisible by
+        viewModel.isImeSwitcherButtonVisible.collectAsStateWithLifecycle()
+    val selectedUserId by viewModel.selectedUserId.collectAsStateWithLifecycle()
 
     DisposableEffect(Unit) { onDispose { viewModel.onHidden() } }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index d7e9c10..9c2fd64 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -30,7 +30,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -47,6 +46,7 @@
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.res.integerResource
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Easings
 import com.android.compose.modifiers.thenIf
 import com.android.internal.R
@@ -86,14 +86,15 @@
     val lineStrokeWidth = with(density) { LINE_STROKE_WIDTH_DP.dp.toPx() }
 
     // All dots that should be rendered on the grid.
-    val dots: List<PatternDotViewModel> by viewModel.dots.collectAsState()
+    val dots: List<PatternDotViewModel> by viewModel.dots.collectAsStateWithLifecycle()
     // The most recently selected dot, if the user is currently dragging.
-    val currentDot: PatternDotViewModel? by viewModel.currentDot.collectAsState()
+    val currentDot: PatternDotViewModel? by viewModel.currentDot.collectAsStateWithLifecycle()
     // The dots selected so far, if the user is currently dragging.
-    val selectedDots: List<PatternDotViewModel> by viewModel.selectedDots.collectAsState()
-    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
-    val isAnimationEnabled: Boolean by viewModel.isPatternVisible.collectAsState()
-    val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
+    val selectedDots: List<PatternDotViewModel> by
+        viewModel.selectedDots.collectAsStateWithLifecycle()
+    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsStateWithLifecycle()
+    val isAnimationEnabled: Boolean by viewModel.isPatternVisible.collectAsStateWithLifecycle()
+    val animateFailure: Boolean by viewModel.animateFailure.collectAsStateWithLifecycle()
 
     // Map of animatables for the scale of each dot, keyed by dot.
     val dotScalingAnimatables = remember(dots) { dots.associateWith { Animatable(1f) } }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 5651a46..64ace2f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -33,7 +33,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -49,6 +48,7 @@
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Easings
 import com.android.compose.grid.VerticalGrid
 import com.android.compose.modifiers.thenIf
@@ -74,12 +74,13 @@
 ) {
     DisposableEffect(Unit) { onDispose { viewModel.onHidden() } }
 
-    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
-    val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
-    val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState()
-    val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
+    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsStateWithLifecycle()
+    val backspaceButtonAppearance by
+        viewModel.backspaceButtonAppearance.collectAsStateWithLifecycle()
+    val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsStateWithLifecycle()
+    val animateFailure: Boolean by viewModel.animateFailure.collectAsStateWithLifecycle()
     val isDigitButtonAnimationEnabled: Boolean by
-        viewModel.isDigitButtonAnimationEnabled.collectAsState()
+        viewModel.isDigitButtonAnimationEnabled.collectAsStateWithLifecycle()
 
     val buttonScaleAnimatables = remember { List(12) { Animatable(1f) } }
     LaunchedEffect(animateFailure) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
index 1a97912..465eade 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
@@ -42,7 +42,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
 import androidx.compose.runtime.mutableStateListOf
@@ -65,6 +64,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.Dialog
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformOutlinedButton
 import com.android.compose.animation.Easings
 import com.android.keyguard.PinShapeAdapter
@@ -86,7 +86,7 @@
     viewModel: PinBouncerViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsState()
+    val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsStateWithLifecycle()
     val shapeAnimations = rememberShapeAnimations(viewModel.pinShapes)
 
     // The display comes in two different flavors:
@@ -119,7 +119,7 @@
     hintedPinLength: Int,
     modifier: Modifier = Modifier,
 ) {
-    val pinInput: PinInputViewModel by viewModel.pinInput.collectAsState()
+    val pinInput: PinInputViewModel by viewModel.pinInput.collectAsStateWithLifecycle()
     // [ClearAll] marker pointing at the beginning of the current pin input.
     // When a new [ClearAll] token is added to the [pinInput], the clear-all animation is played
     // and the marker is advanced manually to the most recent marker. See LaunchedEffect below.
@@ -257,9 +257,10 @@
 
 @Composable
 private fun SimArea(viewModel: PinBouncerViewModel) {
-    val isLockedEsim by viewModel.isLockedEsim.collectAsState()
-    val isSimUnlockingDialogVisible by viewModel.isSimUnlockingDialogVisible.collectAsState()
-    val errorDialogMessage by viewModel.errorDialogMessage.collectAsState()
+    val isLockedEsim by viewModel.isLockedEsim.collectAsStateWithLifecycle()
+    val isSimUnlockingDialogVisible by
+        viewModel.isSimUnlockingDialogVisible.collectAsStateWithLifecycle()
+    val errorDialogMessage by viewModel.errorDialogMessage.collectAsStateWithLifecycle()
     var unlockDialog: Dialog? by remember { mutableStateOf(null) }
     var errorDialog: Dialog? by remember { mutableStateOf(null) }
     val context = LocalView.current.context
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt
index c8e1450..694326d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt
@@ -35,7 +35,7 @@
  * ```
  * @Composable
  * fun YourFunction(viewModel: YourViewModel) {
- *     val selectedUserId by viewModel.selectedUserId.collectAsState()
+ *     val selectedUserId by viewModel.selectedUserId.collectAsStateWithLifecycle()
  *
  *     SelectedUserAwareInputConnection(selectedUserId) {
  *         TextField(...)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
index 8144d15..296fc27 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
@@ -22,12 +22,12 @@
 import androidx.compose.foundation.layout.systemBars
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.staticCompositionLocalOf
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import kotlinx.coroutines.flow.StateFlow
 
 /** The bounds and [CutoutLocation] of the current display. */
@@ -45,7 +45,7 @@
     screenCornerRadius: Float,
     content: @Composable () -> Unit,
 ) {
-    val cutout by displayCutout.collectAsState()
+    val cutout by displayCutout.collectAsStateWithLifecycle()
     val screenCornerRadiusDp = with(LocalDensity.current) { screenCornerRadius.toDp() }
 
     val density = LocalDensity.current
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index ec3c003..feb1f5b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -2,19 +2,28 @@
 
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.FixedSizeEdgeDetector
 import com.android.compose.animation.scene.LowestZIndexScenePicker
 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
@@ -25,6 +34,7 @@
 import com.android.compose.animation.scene.SwipeDirection
 import com.android.compose.animation.scene.observableTransitionState
 import com.android.compose.animation.scene.transitions
+import com.android.systemui.Flags
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
 import com.android.systemui.communal.ui.compose.extensions.allowGestures
@@ -33,33 +43,38 @@
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
 import com.android.systemui.scene.ui.composable.SceneTransitionLayoutDataSource
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 
 object Communal {
     object Elements {
         val Scrim = ElementKey("Scrim", scenePicker = LowestZIndexScenePicker)
-        val Content = ElementKey("CommunalContent")
+        val Grid = ElementKey("CommunalContent")
+        val LockIcon = ElementKey("CommunalLockIcon")
+        val IndicationArea = ElementKey("CommunalIndicationArea")
     }
 }
 
+object AllElements : ElementMatcher {
+    override fun matches(key: ElementKey, scene: SceneKey) = true
+}
+
 val sceneTransitions = transitions {
     to(CommunalScenes.Communal, key = CommunalTransitionKeys.SimpleFade) {
         spec = tween(durationMillis = 250)
-        fade(Communal.Elements.Scrim)
-        fade(Communal.Elements.Content)
+        fade(AllElements)
     }
     to(CommunalScenes.Communal) {
         spec = tween(durationMillis = 1000)
-        translate(Communal.Elements.Content, Edge.Right)
-        timestampRange(startMillis = 167, endMillis = 334) {
-            fade(Communal.Elements.Scrim)
-            fade(Communal.Elements.Content)
-        }
+        translate(Communal.Elements.Grid, Edge.Right)
+        timestampRange(startMillis = 167, endMillis = 334) { fade(AllElements) }
     }
     to(CommunalScenes.Blank) {
         spec = tween(durationMillis = 1000)
-        translate(Communal.Elements.Content, Edge.Right)
-        timestampRange(endMillis = 167) { fade(Communal.Elements.Content) }
+        translate(Communal.Elements.Grid, Edge.Right)
+        timestampRange(endMillis = 167) {
+            fade(Communal.Elements.Grid)
+            fade(Communal.Elements.IndicationArea)
+            fade(Communal.Elements.LockIcon)
+        }
         timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
     }
 }
@@ -75,12 +90,15 @@
     modifier: Modifier = Modifier,
     viewModel: CommunalViewModel,
     dataSourceDelegator: SceneDataSourceDelegator,
-    dialogFactory: SystemUIDialogFactory,
     colors: CommunalColors,
+    content: CommunalContent,
 ) {
     val coroutineScope = rememberCoroutineScope()
-    val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank)
-    val touchesAllowed by viewModel.touchesAllowed.collectAsState(initial = false)
+    val currentSceneKey: SceneKey by
+        viewModel.currentScene.collectAsStateWithLifecycle(CommunalScenes.Blank)
+    val touchesAllowed by viewModel.touchesAllowed.collectAsStateWithLifecycle(initialValue = false)
+    val showGestureIndicator by
+        viewModel.showGestureIndicator.collectAsStateWithLifecycle(initialValue = false)
     val state: MutableSceneTransitionLayoutState = remember {
         MutableSceneTransitionLayoutState(
             initialScene = currentSceneKey,
@@ -119,7 +137,19 @@
                 )
         ) {
             // This scene shows nothing only allowing for transitions to the communal scene.
-            Box(modifier = Modifier.fillMaxSize())
+            // TODO(b/339667383): remove this temporary swipe gesture handle
+            Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.End) {
+                if (showGestureIndicator && Flags.glanceableHubGestureHandle()) {
+                    Box(
+                        modifier =
+                            Modifier.height(220.dp)
+                                .width(4.dp)
+                                .align(Alignment.CenterVertically)
+                                .background(color = Color.White, RoundedCornerShape(4.dp))
+                    )
+                    Spacer(modifier = Modifier.width(12.dp))
+                }
+            }
         }
 
         scene(
@@ -127,7 +157,7 @@
             userActions =
                 mapOf(Swipe(SwipeDirection.Right, fromSource = Edge.Left) to CommunalScenes.Blank)
         ) {
-            CommunalScene(viewModel, colors, dialogFactory, modifier = modifier)
+            CommunalScene(colors, content)
         }
     }
 
@@ -139,12 +169,11 @@
 /** Scene containing the glanceable hub UI. */
 @Composable
 private fun SceneScope.CommunalScene(
-    viewModel: CommunalViewModel,
     colors: CommunalColors,
-    dialogFactory: SystemUIDialogFactory,
+    content: CommunalContent,
     modifier: Modifier = Modifier,
 ) {
-    val backgroundColor by colors.backgroundColor.collectAsState()
+    val backgroundColor by colors.backgroundColor.collectAsStateWithLifecycle()
 
     Box(
         modifier =
@@ -152,7 +181,5 @@
                 .fillMaxSize()
                 .background(Color(backgroundColor.toArgb())),
     )
-    Box(modifier.element(Communal.Elements.Content)) {
-        CommunalHub(viewModel = viewModel, dialogFactory = dialogFactory)
-    }
+    with(content) { Content(modifier = modifier) }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
new file mode 100644
index 0000000..77665155
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.compose
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.unit.IntRect
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
+import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.statusbar.phone.SystemUIDialogFactory
+import javax.inject.Inject
+
+/** Renders the content of the glanceable hub. */
+class CommunalContent
+@Inject
+constructor(
+    private val viewModel: CommunalViewModel,
+    private val dialogFactory: SystemUIDialogFactory,
+    private val lockSection: LockSection,
+) {
+
+    @Composable
+    fun SceneScope.Content(modifier: Modifier = Modifier) {
+        Layout(
+            modifier = modifier.fillMaxSize(),
+            content = {
+                CommunalHub(
+                    viewModel = viewModel,
+                    dialogFactory = dialogFactory,
+                    modifier = Modifier.element(Communal.Elements.Grid)
+                )
+                with(lockSection) {
+                    LockIcon(
+                        overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer,
+                        modifier = Modifier.element(Communal.Elements.LockIcon)
+                    )
+                }
+            }
+        ) { measurables, constraints ->
+            val communalGridMeasurable = measurables[0]
+            val lockIconMeasurable = measurables[1]
+
+            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],
+                )
+
+            val communalGridPlaceable =
+                communalGridMeasurable.measure(
+                    noMinConstraints.copy(maxHeight = lockIconBounds.top)
+                )
+
+            layout(constraints.maxWidth, constraints.maxHeight) {
+                communalGridPlaceable.place(
+                    x = 0,
+                    y = 0,
+                )
+                lockIconPlaceable.place(
+                    x = lockIconBounds.left,
+                    y = lockIconBounds.top,
+                )
+            }
+        }
+    }
+}
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 6fe5cef..cd27d57 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
@@ -48,6 +48,7 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.wrapContentHeight
@@ -78,7 +79,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -88,8 +88,6 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.alpha
-import androidx.compose.ui.draw.scale
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
@@ -108,7 +106,6 @@
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.semantics.CustomAccessibilityAction
 import androidx.compose.ui.semantics.contentDescription
@@ -122,9 +119,11 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.unit.times
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.compose.ui.window.Popup
-import androidx.core.view.setPadding
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.window.layout.WindowMetricsCalculator
 import com.android.compose.modifiers.thenIf
 import com.android.compose.theme.LocalAndroidColorScheme
@@ -157,20 +156,21 @@
     onOpenWidgetPicker: (() -> Unit)? = null,
     onEditDone: (() -> Unit)? = null,
 ) {
-    val communalContent by viewModel.communalContent.collectAsState(initial = emptyList())
-    val currentPopup by viewModel.currentPopup.collectAsState(initial = null)
+    val communalContent by
+        viewModel.communalContent.collectAsStateWithLifecycle(initialValue = emptyList())
+    val currentPopup by viewModel.currentPopup.collectAsStateWithLifecycle(initialValue = null)
     var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
     var toolbarSize: IntSize? by remember { mutableStateOf(null) }
     var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
     var isDraggingToRemove by remember { mutableStateOf(false) }
     val gridState = rememberLazyGridState()
     val contentListState = rememberContentListState(widgetConfigurator, communalContent, viewModel)
-    val reorderingWidgets by viewModel.reorderingWidgets.collectAsState()
-    val selectedKey = viewModel.selectedKey.collectAsState()
+    val reorderingWidgets by viewModel.reorderingWidgets.collectAsStateWithLifecycle()
+    val selectedKey = viewModel.selectedKey.collectAsStateWithLifecycle()
     val removeButtonEnabled by remember {
         derivedStateOf { selectedKey.value != null || reorderingWidgets }
     }
-    val isEmptyState by viewModel.isEmptyState.collectAsState(initial = false)
+    val isEmptyState by viewModel.isEmptyState.collectAsStateWithLifecycle(initialValue = false)
 
     val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
     val contentOffset = beforeContentPadding(contentPadding).toOffset()
@@ -265,17 +265,6 @@
             }
         }
 
-        // TODO(b/326060686): Remove this once keyguard indication area can persist over hub
-        if (viewModel is CommunalViewModel) {
-            val isUnlocked by viewModel.deviceUnlocked.collectAsState(initial = false)
-            LockStateIcon(
-                modifier =
-                    Modifier.align(Alignment.BottomCenter)
-                        .padding(bottom = Dimensions.LockIconBottomPadding),
-                isUnlocked = isUnlocked,
-            )
-        }
-
         if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) {
             Toolbar(
                 setToolbarSize = { toolbarSize = it },
@@ -315,9 +304,9 @@
 
         if (viewModel is CommunalViewModel && dialogFactory != null) {
             val isEnableWidgetDialogShowing by
-                viewModel.isEnableWidgetDialogShowing.collectAsState(false)
+                viewModel.isEnableWidgetDialogShowing.collectAsStateWithLifecycle(false)
             val isEnableWorkProfileDialogShowing by
-                viewModel.isEnableWorkProfileDialogShowing.collectAsState(false)
+                viewModel.isEnableWorkProfileDialogShowing.collectAsStateWithLifecycle(false)
 
             EnableWidgetDialog(
                 isEnableWidgetDialogVisible = isEnableWidgetDialogShowing,
@@ -438,8 +427,8 @@
         state = gridState,
         rows = GridCells.Fixed(CommunalContentSize.FULL.span),
         contentPadding = contentPadding,
-        horizontalArrangement = Arrangement.spacedBy(32.dp),
-        verticalArrangement = Arrangement.spacedBy(32.dp),
+        horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
+        verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
     ) {
         items(
             count = list.size,
@@ -452,7 +441,7 @@
                     Dimensions.CardWidth.value,
                     list[index].size.dp().value,
                 )
-            val cardModifier = Modifier.size(width = size.width.dp, height = size.height.dp)
+            val cardModifier = Modifier.requiredSize(width = size.width.dp, height = size.height.dp)
             if (viewModel.isEditMode && dragDropState != null) {
                 val selected by
                     remember(index) { derivedStateOf { list[index].key == selectedKey.value } }
@@ -549,26 +538,6 @@
     }
 }
 
-@Composable
-private fun LockStateIcon(
-    isUnlocked: Boolean,
-    modifier: Modifier = Modifier,
-) {
-    val colors = LocalAndroidColorScheme.current
-    val resource =
-        if (isUnlocked) {
-            R.drawable.ic_unlocked
-        } else {
-            R.drawable.ic_lock
-        }
-    Icon(
-        painter = painterResource(id = resource),
-        contentDescription = null,
-        tint = colors.onPrimaryContainer,
-        modifier = modifier.size(Dimensions.LockIconSize),
-    )
-}
-
 /**
  * Toolbar that contains action buttons to
  * 1) open the widget picker
@@ -826,12 +795,10 @@
                 containerColor = colors.primary,
                 contentColor = colors.onPrimary,
             ),
-        shape = RoundedCornerShape(80.dp, 40.dp, 80.dp, 40.dp)
+        shape = RoundedCornerShape(68.dp, 34.dp, 68.dp, 34.dp)
     ) {
         Column(
-            modifier = Modifier.fillMaxSize().padding(horizontal = 82.dp),
-            verticalArrangement =
-                Arrangement.spacedBy(Dimensions.Spacing, Alignment.CenterVertically),
+            modifier = Modifier.fillMaxSize().padding(vertical = 38.dp, horizontal = 70.dp),
             horizontalAlignment = Alignment.CenterHorizontally,
         ) {
             Icon(
@@ -839,11 +806,13 @@
                 contentDescription = stringResource(R.string.cta_label_to_open_widget_picker),
                 modifier = Modifier.size(Dimensions.IconSize),
             )
+            Spacer(modifier = Modifier.size(6.dp))
             Text(
                 text = stringResource(R.string.cta_label_to_edit_widget),
-                style = MaterialTheme.typography.titleLarge,
+                style = MaterialTheme.typography.titleMedium,
                 textAlign = TextAlign.Center,
             )
+            Spacer(modifier = Modifier.size(20.dp))
             Row(
                 modifier = Modifier.fillMaxWidth(),
                 horizontalArrangement = Arrangement.Center,
@@ -859,9 +828,10 @@
                 ) {
                     Text(
                         text = stringResource(R.string.cta_tile_button_to_dismiss),
+                        fontSize = 12.sp,
                     )
                 }
-                Spacer(modifier = Modifier.size(Dimensions.Spacing))
+                Spacer(modifier = Modifier.size(14.dp))
                 Button(
                     colors =
                         ButtonDefaults.buttonColors(
@@ -873,6 +843,7 @@
                 ) {
                     Text(
                         text = stringResource(R.string.cta_tile_button_to_open_widget_editor),
+                        fontSize = 12.sp,
                     )
                 }
             }
@@ -892,7 +863,7 @@
     contentListState: ContentListState,
 ) {
     val context = LocalContext.current
-    val isFocusable by viewModel.isFocusable.collectAsState(initial = false)
+    val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
     val accessibilityLabel =
         remember(model, context) {
             model.providerInfo.loadLabel(context.packageManager).toString().trim()
@@ -900,7 +871,7 @@
     val clickActionLabel = stringResource(R.string.accessibility_action_label_select_widget)
     val removeWidgetActionLabel = stringResource(R.string.accessibility_action_label_remove_widget)
     val placeWidgetActionLabel = stringResource(R.string.accessibility_action_label_place_widget)
-    val selectedKey by viewModel.selectedKey.collectAsState()
+    val selectedKey by viewModel.selectedKey.collectAsStateWithLifecycle()
     val selectedIndex =
         selectedKey?.let { key -> contentListState.list.indexOfFirst { it.key == key } }
     Box(
@@ -958,10 +929,14 @@
                 model.appWidgetHost
                     .createViewForCommunal(context, model.appWidgetId, model.providerInfo)
                     .apply {
-                        updateAppWidgetSize(Bundle.EMPTY, listOf(size))
-                        // Remove the extra padding applied to AppWidgetHostView to allow widgets to
-                        // occupy the entire box.
-                        setPadding(0)
+                        updateAppWidgetSize(
+                            /* newOptions = */ Bundle(),
+                            /* minWidth = */ size.width.toInt(),
+                            /* minHeight = */ size.height.toInt(),
+                            /* maxWidth = */ size.width.toInt(),
+                            /* maxHeight = */ size.height.toInt(),
+                            /* ignorePadding = */ true
+                        )
                         accessibilityDelegate = viewModel.widgetAccessibilityDelegate
                     }
             },
@@ -1141,7 +1116,7 @@
 @Composable
 fun AccessibilityContainer(viewModel: BaseCommunalViewModel, content: @Composable () -> Unit) {
     val context = LocalContext.current
-    val isFocusable by viewModel.isFocusable.collectAsState(initial = false)
+    val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
     Box(
         modifier =
             Modifier.fillMaxWidth().wrapContentHeight().thenIf(
@@ -1184,7 +1159,11 @@
 @Composable
 private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): PaddingValues {
     if (!isEditMode || toolbarSize == null) {
-        return PaddingValues(start = 48.dp, end = 48.dp, top = Dimensions.GridTopSpacing)
+        return PaddingValues(
+            start = Dimensions.ItemSpacing,
+            end = Dimensions.ItemSpacing,
+            top = Dimensions.GridTopSpacing,
+        )
     }
     val context = LocalContext.current
     val density = LocalDensity.current
@@ -1247,18 +1226,19 @@
 }
 
 object Dimensions {
-    val CardWidth = 424.dp
-    val CardHeightFull = 596.dp
-    val CardHeightHalf = 282.dp
-    val CardHeightThird = 177.33.dp
-    val CardOutlineWidth = 3.dp
-    val GridTopSpacing = 64.dp
+    val CardHeightFull = 530.dp
+    val GridTopSpacing = 114.dp
     val GridHeight = CardHeightFull + GridTopSpacing
-    val Spacing = 16.dp
+    val ItemSpacing = 50.dp
+    val CardHeightHalf = (CardHeightFull - ItemSpacing) / 2
+    val CardHeightThird = (CardHeightFull - (2 * ItemSpacing)) / 3
+    val CardWidth = 360.dp
+    val CardOutlineWidth = 3.dp
+    val Spacing = ItemSpacing / 2
 
     // The sizing/padding of the toolbar in glanceable hub edit mode
     val ToolbarPaddingTop = 27.dp
-    val ToolbarPaddingHorizontal = 16.dp
+    val ToolbarPaddingHorizontal = ItemSpacing
     val ToolbarButtonPaddingHorizontal = 24.dp
     val ToolbarButtonPaddingVertical = 16.dp
     val ButtonPadding =
@@ -1266,10 +1246,7 @@
             vertical = ToolbarButtonPaddingVertical,
             horizontal = ToolbarButtonPaddingHorizontal,
         )
-    val IconSize = 48.dp
-
-    val LockIconSize = 52.dp
-    val LockIconBottomPadding = 70.dp
+    val IconSize = 40.dp
 }
 
 private object Colors {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
index e77ade9..17dac7e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
@@ -18,11 +18,11 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.produceState
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.window.layout.WindowInfoTracker
 import com.android.systemui.fold.ui.helper.FoldPosture
 import com.android.systemui.fold.ui.helper.foldPostureInternal
@@ -32,7 +32,8 @@
 fun foldPosture(): State<FoldPosture> {
     val context = LocalContext.current
     val infoTracker = remember(context) { WindowInfoTracker.getOrCreate(context) }
-    val layoutInfo by infoTracker.windowLayoutInfo(context).collectAsState(initial = null)
+    val layoutInfo by
+        infoTracker.windowLayoutInfo(context).collectAsStateWithLifecycle(initialValue = null)
 
     return produceState<FoldPosture>(
         initialValue = FoldPosture.Folded,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
index a8d801a..67840c7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
@@ -29,7 +29,6 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
 import androidx.compose.ui.Alignment
@@ -37,6 +36,7 @@
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.theme.PlatformTheme
 import com.android.systemui.keyboard.stickykeys.shared.model.Locked
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
@@ -57,7 +57,7 @@
 
 @Composable
 fun StickyKeysIndicator(viewModel: StickyKeysIndicatorViewModel) {
-    val stickyKeys by viewModel.indicatorContent.collectAsState(emptyMap())
+    val stickyKeys by viewModel.indicatorContent.collectAsStateWithLifecycle(emptyMap())
     StickyKeysIndicator(stickyKeys)
 }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index 4bef9ef..ca4ff83 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -18,12 +18,13 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
@@ -51,7 +52,7 @@
         modifier: Modifier = Modifier,
     ) {
         val coroutineScope = rememberCoroutineScope()
-        val blueprintId by viewModel.blueprintId(coroutineScope).collectAsState()
+        val blueprintId by viewModel.blueprintId(coroutineScope).collectAsStateWithLifecycle()
         val view = LocalView.current
         DisposableEffect(view) {
             clockInteractor.clockEventController.registerListeners(view)
@@ -60,6 +61,6 @@
         }
 
         val blueprint = blueprintByBlueprintId[blueprintId] ?: return
-        with(blueprint) { Content(modifier) }
+        with(blueprint) { Content(modifier.sysuiResTag("keyguard_root_view")) }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
index 472484a..4555f13 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
@@ -26,13 +26,13 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
 
 /** Container for lockscreen content that handles long-press to bring up the settings menu. */
@@ -42,7 +42,8 @@
     modifier: Modifier = Modifier,
     content: @Composable BoxScope.(onSettingsMenuPlaces: (coordinates: Rect?) -> Unit) -> Unit,
 ) {
-    val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
+    val isEnabled: Boolean by
+        viewModel.isLongPressHandlingEnabled.collectAsStateWithLifecycle(initialValue = false)
     val (settingsMenuBounds, setSettingsMenuBounds) = remember { mutableStateOf<Rect?>(null) }
     val interactionSource = remember { MutableInteractionSource() }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
index 8129e41..ba25719 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
@@ -21,11 +21,11 @@
 import androidx.compose.foundation.layout.systemBars
 import androidx.compose.foundation.layout.union
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalDensity
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.plugins.clocks.ClockController
@@ -37,7 +37,7 @@
 fun rememberBurnIn(
     clockInteractor: KeyguardClockInteractor,
 ): BurnInState {
-    val clock by clockInteractor.currentClock.collectAsState()
+    val clock by clockInteractor.currentClock.collectAsStateWithLifecycle()
 
     val (smartspaceTop, onSmartspaceTopChanged) = remember { mutableStateOf<Float?>(null) }
     val (smallClockTop, onSmallClockTopChanged) = remember { mutableStateOf<Float?>(null) }
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 3152535..a39fa64 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
@@ -22,15 +22,16 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.unit.IntRect
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
+import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
 import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
@@ -67,8 +68,8 @@
     override fun SceneScope.Content(modifier: Modifier) {
         val isUdfpsVisible = viewModel.isUdfpsVisible
         val shouldUseSplitNotificationShade by
-            viewModel.shouldUseSplitNotificationShade.collectAsState()
-        val unfoldTranslations by viewModel.unfoldTranslations.collectAsState()
+            viewModel.shouldUseSplitNotificationShade.collectAsStateWithLifecycle()
+        val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
 
         LockscreenLongPress(
             viewModel = viewModel.longPress,
@@ -129,7 +130,7 @@
                     with(lockSection) { LockIcon() }
 
                     // Aligned to bottom and constrained to below the lock icon.
-                    Column(modifier = Modifier.fillMaxWidth()) {
+                    Column(modifier = Modifier.fillMaxWidth().sysuiResTag("keyguard_bottom_area")) {
                         if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
                             with(ambientIndicationSectionOptional.get()) {
                                 AmbientIndication(modifier = Modifier.fillMaxWidth())
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 9d31955..c83f62c 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
@@ -22,13 +22,13 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.unit.IntRect
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
 import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
@@ -70,8 +70,8 @@
     override fun SceneScope.Content(modifier: Modifier) {
         val isUdfpsVisible = viewModel.isUdfpsVisible
         val shouldUseSplitNotificationShade by
-            viewModel.shouldUseSplitNotificationShade.collectAsState()
-        val unfoldTranslations by viewModel.unfoldTranslations.collectAsState()
+            viewModel.shouldUseSplitNotificationShade.collectAsStateWithLifecycle()
+        val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
 
         LockscreenLongPress(
             viewModel = viewModel.longPress,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
index c109e51..aaf49ff 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.keyguard.ui.composable.modifier
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -25,6 +24,7 @@
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.boundsInWindow
 import androidx.compose.ui.layout.onPlaced
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.keyguard.ui.viewmodel.BurnInScaleViewModel
@@ -44,8 +44,10 @@
     val translationYState = remember { mutableStateOf(0F) }
     val copiedParams = params.copy(translationY = { translationYState.value })
     val burnIn = viewModel.movement(copiedParams)
-    val translationX by burnIn.map { it.translationX.toFloat() }.collectAsState(initial = 0f)
-    val translationY by burnIn.map { it.translationY.toFloat() }.collectAsState(initial = 0f)
+    val translationX by
+        burnIn.map { it.translationX.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
+    val translationY by
+        burnIn.map { it.translationY.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
     translationYState.value = translationY
     val scaleViewModel by
         burnIn
@@ -55,18 +57,14 @@
                     scaleClockOnly = it.scaleClockOnly,
                 )
             }
-            .collectAsState(initial = BurnInScaleViewModel())
+            .collectAsStateWithLifecycle(initialValue = BurnInScaleViewModel())
 
     return this.graphicsLayer {
-        val scale =
-            when {
-                scaleViewModel.scaleClockOnly && isClock -> scaleViewModel.scale
-                else -> 1f
-            }
-
         this.translationX = if (isClock) 0F else translationX
         this.translationY = translationY
         this.alpha = alpha
+
+        val scale = if (scaleViewModel.scaleClockOnly) scaleViewModel.scale else 1f
         this.scaleX = scale
         this.scaleY = scale
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 09ec76d..218779d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -25,13 +25,13 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.core.view.contains
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
 import com.android.systemui.customization.R
@@ -59,9 +59,11 @@
         onTopChanged: (top: Float?) -> Unit,
         modifier: Modifier = Modifier,
     ) {
-        val currentClock by viewModel.currentClock.collectAsState()
+        val currentClock by viewModel.currentClock.collectAsStateWithLifecycle()
         val smallTopMargin by
-            viewModel.smallClockTopMargin.collectAsState(viewModel.getSmallClockTopMargin())
+            viewModel.smallClockTopMargin.collectAsStateWithLifecycle(
+                viewModel.getSmallClockTopMargin()
+            )
         if (currentClock?.smallClock?.view == null) {
             return
         }
@@ -89,7 +91,7 @@
 
     @Composable
     fun SceneScope.LargeClock(burnInParams: BurnInParameters, modifier: Modifier = Modifier) {
-        val currentClock by viewModel.currentClock.collectAsState()
+        val currentClock by viewModel.currentClock.collectAsStateWithLifecycle()
         if (currentClock?.largeClock?.view == null) {
             return
         }
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 9f02201..4129c25 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
@@ -22,6 +22,7 @@
 import android.view.WindowManager
 import androidx.compose.runtime.Composable
 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
@@ -68,7 +69,7 @@
     private val notificationPanelView: NotificationPanelView,
 ) {
     @Composable
-    fun SceneScope.LockIcon(modifier: Modifier = Modifier) {
+    fun SceneScope.LockIcon(overrideColor: Color? = null, modifier: Modifier = Modifier) {
         if (!KeyguardBottomAreaRefactor.isEnabled && !DeviceEntryUdfpsRefactor.isEnabled) {
             return
         }
@@ -93,6 +94,7 @@
                                 deviceEntryBackgroundViewModel.get(),
                                 falsingManager.get(),
                                 vibratorHelper.get(),
+                                overrideColor,
                             )
                         }
                     } else {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
index c37d626..3ca2b9c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
@@ -18,9 +18,9 @@
 
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardMediaViewModel
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
@@ -40,7 +40,7 @@
 
     @Composable
     fun SceneScope.KeyguardMediaCarousel() {
-        val isMediaVisible by keyguardMediaViewModel.isMediaVisible.collectAsState()
+        val isMediaVisible by keyguardMediaViewModel.isMediaVisible.collectAsStateWithLifecycle()
 
         MediaCarousel(
             isVisible = isMediaVisible,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index f48fa88..7f80dfa 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -20,13 +20,13 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.Flags
@@ -40,16 +40,19 @@
 import com.android.systemui.res.R
 import com.android.systemui.shade.LargeScreenHeaderHelper
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import dagger.Lazy
 import javax.inject.Inject
 
 @SysUISingleton
 class NotificationSection
 @Inject
 constructor(
+    private val stackScrollView: Lazy<NotificationScrollView>,
     private val viewModel: NotificationsPlaceholderViewModel,
     private val aodBurnInViewModel: AodBurnInViewModel,
     sharedNotificationContainer: SharedNotificationContainer,
@@ -88,9 +91,9 @@
     @Composable
     fun SceneScope.Notifications(burnInParams: BurnInParameters?, modifier: Modifier = Modifier) {
         val shouldUseSplitNotificationShade by
-            lockscreenContentViewModel.shouldUseSplitNotificationShade.collectAsState()
+            lockscreenContentViewModel.shouldUseSplitNotificationShade.collectAsStateWithLifecycle()
         val areNotificationsVisible by
-            lockscreenContentViewModel.areNotificationsVisible.collectAsState()
+            lockscreenContentViewModel.areNotificationsVisible.collectAsStateWithLifecycle()
         val splitShadeTopMargin: Dp =
             if (Flags.centralizedStatusBarHeightFix()) {
                 LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp
@@ -103,6 +106,7 @@
         }
 
         ConstrainedNotificationStack(
+            stackScrollView = stackScrollView.get(),
             viewModel = viewModel,
             modifier =
                 modifier
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index fc8b3b9..44bda95 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -26,7 +26,6 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -34,6 +33,7 @@
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
@@ -160,7 +160,7 @@
     private fun Weather(
         modifier: Modifier = Modifier,
     ) {
-        val isVisible by keyguardSmartspaceViewModel.isWeatherVisible.collectAsState()
+        val isVisible by keyguardSmartspaceViewModel.isWeatherVisible.collectAsStateWithLifecycle()
         if (!isVisible) {
             return
         }
@@ -187,7 +187,7 @@
     private fun Date(
         modifier: Modifier = Modifier,
     ) {
-        val isVisible by keyguardSmartspaceViewModel.isDateVisible.collectAsState()
+        val isVisible by keyguardSmartspaceViewModel.isDateVisible.collectAsStateWithLifecycle()
         if (!isVisible) {
             return
         }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index 63c70c9..0673153 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -25,7 +25,6 @@
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
@@ -33,10 +32,10 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntOffset
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.SceneTransitionLayout
 import com.android.compose.modifiers.thenIf
-import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
 import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.smallClockScene
@@ -62,9 +61,9 @@
     fun DefaultClockLayout(
         modifier: Modifier = Modifier,
     ) {
-        val currentClockLayout by clockViewModel.currentClockLayout.collectAsState()
+        val currentClockLayout by clockViewModel.currentClockLayout.collectAsStateWithLifecycle()
         val hasCustomPositionUpdatedAnimation by
-            clockViewModel.hasCustomPositionUpdatedAnimation.collectAsState()
+            clockViewModel.hasCustomPositionUpdatedAnimation.collectAsStateWithLifecycle()
         val currentScene =
             when (currentClockLayout) {
                 KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK ->
@@ -80,7 +79,7 @@
             }
 
         SceneTransitionLayout(
-            modifier = modifier.sysuiResTag("keyguard_clock_container"),
+            modifier = modifier,
             currentScene = currentScene,
             onChangeScene = {},
             transitions = ClockTransition.defaultClockTransitions,
@@ -133,7 +132,7 @@
     @Composable
     private fun SceneScope.LargeClockWithSmartSpace(shouldOffSetClockToOneHalf: Boolean = false) {
         val burnIn = rememberBurnIn(clockInteractor)
-        val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
+        val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsStateWithLifecycle()
 
         LaunchedEffect(isLargeClockVisible) {
             if (isLargeClockVisible) {
@@ -170,8 +169,8 @@
     @Composable
     private fun SceneScope.WeatherLargeClockWithSmartSpace(modifier: Modifier = Modifier) {
         val burnIn = rememberBurnIn(clockInteractor)
-        val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
-        val currentClockState = clockViewModel.currentClock.collectAsState()
+        val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsStateWithLifecycle()
+        val currentClockState = clockViewModel.currentClock.collectAsStateWithLifecycle()
 
         LaunchedEffect(isLargeClockVisible) {
             if (isLargeClockVisible) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 7c8cc194..cf2e895 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -38,7 +38,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
@@ -64,16 +63,19 @@
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.lerp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.NestedScrollBehavior
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.height
+import com.android.compose.modifiers.thenIf
 import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
 import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
 import com.android.systemui.res.R
 import com.android.systemui.scene.session.ui.composable.SaveableSession
 import com.android.systemui.scene.session.ui.composable.rememberSession
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.shade.ui.composable.ShadeHeader
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
@@ -110,7 +112,7 @@
     modifier: Modifier = Modifier,
     isPeekFromBottom: Boolean = false,
 ) {
-    val headsUpHeight = viewModel.headsUpHeight.collectAsState()
+    val headsUpHeight = viewModel.headsUpHeight.collectAsStateWithLifecycle()
 
     Element(
         Notifications.Elements.HeadsUpNotificationPlaceholder,
@@ -136,6 +138,7 @@
 /** Adds the space where notification stack should appear in the scene. */
 @Composable
 fun SceneScope.ConstrainedNotificationStack(
+    stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
     modifier: Modifier = Modifier,
 ) {
@@ -144,6 +147,7 @@
             modifier.onSizeChanged { viewModel.onConstrainedAvailableSpaceChanged(it.height) }
     ) {
         NotificationPlaceholder(
+            stackScrollView = stackScrollView,
             viewModel = viewModel,
             modifier = Modifier.fillMaxSize(),
         )
@@ -165,6 +169,7 @@
     viewModel: NotificationsPlaceholderViewModel,
     maxScrimTop: () -> Float,
     shouldPunchHoleBehindScrim: Boolean,
+    shadeMode: ShadeMode,
     modifier: Modifier = Modifier,
 ) {
     val coroutineScope = rememberCoroutineScope()
@@ -175,9 +180,10 @@
         shadeSession.rememberSaveableSession(saver = ScrollState.Saver, key = null) {
             ScrollState(initial = 0)
         }
-    val syntheticScroll = viewModel.syntheticScroll.collectAsState(0f)
-    val isCurrentGestureOverscroll = viewModel.isCurrentGestureOverscroll.collectAsState(false)
-    val expansionFraction by viewModel.expandFraction.collectAsState(0f)
+    val syntheticScroll = viewModel.syntheticScroll.collectAsStateWithLifecycle(0f)
+    val isCurrentGestureOverscroll =
+        viewModel.isCurrentGestureOverscroll.collectAsStateWithLifecycle(false)
+    val expansionFraction by viewModel.expandFraction.collectAsStateWithLifecycle(0f)
 
     val navBarHeight =
         with(density) { WindowInsets.systemBars.asPaddingValues().calculateBottomPadding().toPx() }
@@ -190,7 +196,8 @@
      */
     val stackHeight = remember { mutableIntStateOf(0) }
 
-    val scrimRounding = viewModel.shadeScrimRounding.collectAsState(ShadeScrimRounding())
+    val scrimRounding =
+        viewModel.shadeScrimRounding.collectAsStateWithLifecycle(ShadeScrimRounding())
 
     // the offset for the notifications scrim. Its upper bound is 0, and its lower bound is
     // calculated in minScrimOffset. The scrim is the same height as the screen minus the
@@ -242,6 +249,27 @@
             }
     }
 
+    val scrimNestedScrollConnection =
+        shadeSession.rememberSession(
+            scrimOffset,
+            maxScrimTop,
+            minScrimTop,
+            isCurrentGestureOverscroll,
+        ) {
+            NotificationScrimNestedScrollConnection(
+                scrimOffset = { scrimOffset.value },
+                snapScrimOffset = { value -> coroutineScope.launch { scrimOffset.snapTo(value) } },
+                animateScrimOffset = { value ->
+                    coroutineScope.launch { scrimOffset.animateTo(value) }
+                },
+                minScrimOffset = minScrimOffset,
+                maxScrimOffset = 0f,
+                contentHeight = { stackHeight.intValue.toFloat() },
+                minVisibleScrimHeight = minVisibleScrimHeight,
+                isCurrentGestureOverscroll = { isCurrentGestureOverscroll.value },
+            )
+        }
+
     Box(
         modifier =
             modifier
@@ -310,37 +338,16 @@
                     .debugBackground(viewModel, DEBUG_BOX_COLOR)
         ) {
             NotificationPlaceholder(
+                stackScrollView = stackScrollView,
                 viewModel = viewModel,
                 modifier =
                     Modifier.verticalNestedScrollToScene(
                             topBehavior = NestedScrollBehavior.EdgeWithPreview,
                             isExternalOverscrollGesture = { isCurrentGestureOverscroll.value }
                         )
-                        .nestedScroll(
-                            shadeSession.rememberSession(
-                                scrimOffset,
-                                maxScrimTop,
-                                minScrimTop,
-                                isCurrentGestureOverscroll,
-                            ) {
-                                NotificationScrimNestedScrollConnection(
-                                    scrimOffset = { scrimOffset.value },
-                                    snapScrimOffset = { value ->
-                                        coroutineScope.launch { scrimOffset.snapTo(value) }
-                                    },
-                                    animateScrimOffset = { value ->
-                                        coroutineScope.launch { scrimOffset.animateTo(value) }
-                                    },
-                                    minScrimOffset = minScrimOffset,
-                                    maxScrimOffset = 0f,
-                                    contentHeight = { stackHeight.intValue.toFloat() },
-                                    minVisibleScrimHeight = minVisibleScrimHeight,
-                                    isCurrentGestureOverscroll = {
-                                        isCurrentGestureOverscroll.value
-                                    },
-                                )
-                            }
-                        )
+                        .thenIf(shadeMode == ShadeMode.Single) {
+                            Modifier.nestedScroll(scrimNestedScrollConnection)
+                        }
                         .verticalScroll(scrollState)
                         .fillMaxWidth()
                         .notificationStackHeight(
@@ -388,6 +395,7 @@
 
 @Composable
 private fun SceneScope.NotificationPlaceholder(
+    stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
     modifier: Modifier = Modifier,
 ) {
@@ -406,10 +414,8 @@
                             " bounds=${coordinates.boundsInWindow()}"
                     }
                     // NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top will not
-                    viewModel.onStackBoundsChanged(
-                        top = positionInWindow.y,
-                        bottom = positionInWindow.y + coordinates.size.height,
-                    )
+                    stackScrollView.setStackTop(positionInWindow.y)
+                    stackScrollView.setStackBottom(positionInWindow.y + coordinates.size.height)
                 }
     ) {
         content {}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index 73cb72c..b808044 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -36,7 +36,6 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
 import androidx.compose.ui.Alignment
@@ -46,6 +45,7 @@
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
@@ -64,8 +64,8 @@
     viewModel: PeopleViewModel,
     onResult: (PeopleViewModel.Result) -> Unit,
 ) {
-    val priorityTiles by viewModel.priorityTiles.collectAsState()
-    val recentTiles by viewModel.recentTiles.collectAsState()
+    val priorityTiles by viewModel.priorityTiles.collectAsStateWithLifecycle()
+    val recentTiles by viewModel.recentTiles.collectAsStateWithLifecycle()
 
     // Call [onResult] this activity when the ViewModel tells us so.
     LaunchedEffect(viewModel.result) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 2f241ce..e8da4bd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -44,7 +44,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -69,6 +68,7 @@
 import androidx.compose.ui.unit.sp
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.compose.animation.Expandable
 import com.android.compose.animation.scene.SceneScope
@@ -132,8 +132,8 @@
     val context = LocalContext.current
 
     // Collect alphas as soon as we are composed, even when not visible.
-    val alpha by viewModel.alpha.collectAsState()
-    val backgroundAlpha = viewModel.backgroundAlpha.collectAsState()
+    val alpha by viewModel.alpha.collectAsStateWithLifecycle()
+    val backgroundAlpha = viewModel.backgroundAlpha.collectAsStateWithLifecycle()
 
     var security by remember { mutableStateOf<FooterActionsSecurityButtonViewModel?>(null) }
     var foregroundServices by remember {
@@ -181,7 +181,6 @@
     val horizontalPadding = dimensionResource(R.dimen.qs_content_horizontal_padding)
     Row(
         modifier
-            .sysuiResTag("qs_footer_actions")
             .fillMaxWidth()
             .graphicsLayer { this.alpha = alpha }
             .then(backgroundModifier)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
index ca6b343..73a624a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
@@ -21,13 +21,13 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.modifiers.height
 import com.android.compose.modifiers.width
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
@@ -40,13 +40,13 @@
     qsSceneAdapter: QSSceneAdapter,
     modifier: Modifier = Modifier,
 ) {
-    val isShowing by viewModel.isShowing.collectAsState()
+    val isShowing by viewModel.isShowing.collectAsStateWithLifecycle()
     val mirrorAlpha by
         animateFloatAsState(
             targetValue = if (isShowing) 1f else 0f,
             label = "alphaAnimationBrightnessMirrorShowing",
         )
-    val mirrorOffsetAndSize by viewModel.locationAndSize.collectAsState()
+    val mirrorOffsetAndSize by viewModel.locationAndSize.collectAsStateWithLifecycle()
     val offset = IntOffset(0, mirrorOffsetAndSize.yOffset)
 
     Box(modifier = modifier.fillMaxSize().graphicsLayer { alpha = mirrorAlpha }) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 46be6b8..d109988 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -22,18 +22,19 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.MovableElementScenePicker
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.TransitionState
 import com.android.compose.animation.scene.ValueKey
 import com.android.compose.modifiers.thenIf
+import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Companion.Collapsing
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Expanding
@@ -143,7 +144,9 @@
     MovableElement(
         key = QuickSettings.Elements.Content,
         modifier =
-            modifier.fillMaxWidth().layout { measurable, constraints ->
+            modifier.sysuiResTag("quick_settings_panel").fillMaxWidth().layout {
+                measurable,
+                constraints ->
                 val placeable = measurable.measure(constraints)
                 // Use the height of the correct view based on the scene it is being composed in
                 val height = heightProvider().coerceAtLeast(0)
@@ -161,9 +164,11 @@
     state: QSSceneAdapter.State,
     modifier: Modifier = Modifier,
 ) {
-    val qsView by qsSceneAdapter.qsView.collectAsState(null)
+    val qsView by qsSceneAdapter.qsView.collectAsStateWithLifecycle(null)
     val isCustomizing by
-        qsSceneAdapter.isCustomizerShowing.collectAsState(qsSceneAdapter.isCustomizerShowing.value)
+        qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle(
+            qsSceneAdapter.isCustomizerShowing.value
+        )
     QuickSettingsTheme {
         val context = LocalContext.current
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 61ce83a..d76b19f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -51,7 +51,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
@@ -63,6 +62,7 @@
 import androidx.compose.ui.res.colorResource
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.TransitionState
 import com.android.compose.animation.scene.animateSceneFloatAsState
@@ -86,6 +86,7 @@
 import com.android.systemui.scene.session.ui.composable.SaveableSession
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
 import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
 import com.android.systemui.shade.ui.composable.Shade
@@ -162,7 +163,8 @@
 ) {
     val cutoutLocation = LocalDisplayCutout.current.location
 
-    val brightnessMirrorShowing by viewModel.brightnessMirrorViewModel.isShowing.collectAsState()
+    val brightnessMirrorShowing by
+        viewModel.brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle()
     val contentAlpha by
         animateFloatAsState(
             targetValue = if (brightnessMirrorShowing) 0f else 1f,
@@ -197,10 +199,11 @@
                     Modifier.displayCutoutPadding()
                 },
     ) {
-        val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
-        val isCustomizerShowing by viewModel.qsSceneAdapter.isCustomizerShowing.collectAsState()
+        val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
+        val isCustomizerShowing by
+            viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
         val customizingAnimationDuration by
-            viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsState()
+            viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsStateWithLifecycle()
         val screenHeight = LocalRawScreenHeight.current
 
         BackHandler(
@@ -342,10 +345,10 @@
                         viewModel.qsSceneAdapter,
                         { viewModel.qsSceneAdapter.qsHeight },
                         isSplitShade = false,
-                        modifier = Modifier.sysuiResTag("quick_settings_panel")
+                        modifier = Modifier
                     )
 
-                    val isMediaVisible by viewModel.isMediaVisible.collectAsState()
+                    val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
 
                     MediaCarousel(
                         isVisible = isMediaVisible,
@@ -361,7 +364,8 @@
                 isCustomizing = isCustomizing,
                 customizingAnimationDuration = customizingAnimationDuration,
                 lifecycleOwner = lifecycleOwner,
-                modifier = Modifier.align(Alignment.CenterHorizontally),
+                modifier =
+                    Modifier.align(Alignment.CenterHorizontally).sysuiResTag("qs_footer_actions"),
             )
         }
         NotificationScrollingStack(
@@ -370,6 +374,7 @@
             shadeSession = shadeSession,
             maxScrimTop = { screenHeight },
             shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
+            shadeMode = ShadeMode.Single,
             modifier =
                 Modifier.fillMaxWidth().offset { IntOffset(x = 0, y = screenHeight.roundToInt()) },
         )
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 7af9b7b..92b2b4e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -23,7 +23,6 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -34,6 +33,7 @@
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.motionEventSpy
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayout
@@ -68,8 +68,9 @@
     modifier: Modifier = Modifier,
 ) {
     val coroutineScope = rememberCoroutineScope()
-    val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState()
-    val currentDestinations by viewModel.currentDestinationScenes(coroutineScope).collectAsState()
+    val currentSceneKey: SceneKey by viewModel.currentScene.collectAsStateWithLifecycle()
+    val currentDestinations by
+        viewModel.currentDestinationScenes(coroutineScope).collectAsStateWithLifecycle()
     val state: MutableSceneTransitionLayoutState = remember {
         MutableSceneTransitionLayoutState(
             initialScene = currentSceneKey,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index ff9c5a5..cbaa894 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -5,9 +5,8 @@
 import com.android.systemui.bouncer.ui.composable.Bouncer
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.CollapseShadeInstantly
-import com.android.systemui.scene.shared.model.TransitionKeys.GoneToSplitShade
 import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.scene.ui.composable.transitions.bouncerToGoneTransition
 import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsTransition
 import com.android.systemui.scene.ui.composable.transitions.goneToShadeTransition
@@ -17,6 +16,7 @@
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToGoneTransition
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToQuickSettingsTransition
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToShadeTransition
+import com.android.systemui.scene.ui.composable.transitions.lockscreenToSplitShadeTransition
 import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettingsTransition
 import com.android.systemui.shade.ui.composable.Shade
 
@@ -41,7 +41,7 @@
     from(
         Scenes.Gone,
         to = Scenes.Shade,
-        key = GoneToSplitShade,
+        key = ToSplitShade,
     ) {
         goneToSplitShadeTransition()
     }
@@ -59,6 +59,13 @@
     from(
         Scenes.Lockscreen,
         to = Scenes.Shade,
+        key = ToSplitShade,
+    ) {
+        lockscreenToSplitShadeTransition()
+    }
+    from(
+        Scenes.Lockscreen,
+        to = Scenes.Shade,
         key = SlightlyFasterShadeCollapse,
     ) {
         lockscreenToShadeTransition(durationScale = 0.9)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
index 4dc36d6..f14ff76 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
@@ -16,50 +16,10 @@
 
 package com.android.systemui.scene.ui.composable.transitions
 
-import androidx.compose.animation.core.Spring
-import androidx.compose.animation.core.spring
-import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.unit.IntSize
 import com.android.compose.animation.scene.TransitionBuilder
-import com.android.compose.animation.scene.UserActionDistance
-import com.android.compose.animation.scene.UserActionDistanceScope
-import com.android.systemui.notifications.ui.composable.Notifications
-import com.android.systemui.qs.ui.composable.QuickSettings
-import com.android.systemui.shade.ui.composable.Shade
-import com.android.systemui.shade.ui.composable.ShadeHeader
-import kotlin.time.Duration.Companion.milliseconds
 
 fun TransitionBuilder.goneToSplitShadeTransition(
     durationScale: Double = 1.0,
 ) {
-    spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
-    swipeSpec =
-        spring(
-            stiffness = Spring.StiffnessMediumLow,
-            visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
-        )
-    distance =
-        object : UserActionDistance {
-            override fun UserActionDistanceScope.absoluteDistance(
-                fromSceneSize: IntSize,
-                orientation: Orientation,
-            ): Float {
-                return fromSceneSize.height.toFloat() * 2 / 3f
-            }
-        }
-
-    fractionRange(end = .33f) { fade(Shade.Elements.BackgroundScrim) }
-
-    fractionRange(start = .33f) {
-        fade(ShadeHeader.Elements.Clock)
-        fade(ShadeHeader.Elements.CollapsedContentStart)
-        fade(ShadeHeader.Elements.CollapsedContentEnd)
-        fade(ShadeHeader.Elements.PrivacyChip)
-        fade(QuickSettings.Elements.SplitShadeQuickSettings)
-        fade(QuickSettings.Elements.FooterActions)
-        fade(Notifications.Elements.NotificationScrim)
-    }
+    toSplitShadeTransition(durationScale)
 }
-
-private val DefaultDuration = 500.milliseconds
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToSplitShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToSplitShadeTransition.kt
new file mode 100644
index 0000000..70c343c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToSplitShadeTransition.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.ui.composable.transitions
+
+import com.android.compose.animation.scene.TransitionBuilder
+
+fun TransitionBuilder.lockscreenToSplitShadeTransition(
+    durationScale: Double = 1.0,
+) {
+    toSplitShadeTransition(durationScale = durationScale)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToSplitShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToSplitShadeTransition.kt
new file mode 100644
index 0000000..a8315c0
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToSplitShadeTransition.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.IntSize
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.compose.animation.scene.UserActionDistance
+import com.android.compose.animation.scene.UserActionDistanceScope
+import com.android.systemui.notifications.ui.composable.Notifications
+import com.android.systemui.qs.ui.composable.QuickSettings
+import com.android.systemui.shade.ui.composable.Shade
+import com.android.systemui.shade.ui.composable.ShadeHeader
+import kotlin.time.Duration.Companion.milliseconds
+
+fun TransitionBuilder.toSplitShadeTransition(
+    durationScale: Double = 1.0,
+) {
+    spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
+    swipeSpec =
+        spring(
+            stiffness = Spring.StiffnessMediumLow,
+            visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
+        )
+    distance =
+        object : UserActionDistance {
+            override fun UserActionDistanceScope.absoluteDistance(
+                fromSceneSize: IntSize,
+                orientation: Orientation,
+            ): Float {
+                return fromSceneSize.height.toFloat() * 2 / 3f
+            }
+        }
+
+    fractionRange(end = .33f) { fade(Shade.Elements.BackgroundScrim) }
+
+    fractionRange(start = .33f) {
+        fade(ShadeHeader.Elements.Clock)
+        fade(ShadeHeader.Elements.CollapsedContentStart)
+        fade(ShadeHeader.Elements.CollapsedContentEnd)
+        fade(ShadeHeader.Elements.PrivacyChip)
+        fade(QuickSettings.Elements.SplitShadeQuickSettings)
+        fade(QuickSettings.Elements.FooterActions)
+        fade(Notifications.Elements.NotificationScrim)
+    }
+}
+
+private val DefaultDuration = 500.milliseconds
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index d528736..00ef11d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -30,13 +30,13 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.ReadOnlyComposable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexScenePicker
 import com.android.compose.animation.scene.SceneScope
@@ -51,7 +51,7 @@
     modifier: Modifier = Modifier,
     content: @Composable () -> Unit,
 ) {
-    val backgroundScene by viewModel.backgroundScene.collectAsState()
+    val backgroundScene by viewModel.backgroundScene.collectAsStateWithLifecycle()
 
     Box(modifier) {
         if (backgroundScene == Scenes.Lockscreen) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 709a416..ac3e015 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -35,7 +35,6 @@
 import androidx.compose.material3.ColorScheme
 import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
@@ -52,6 +51,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.max
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexScenePicker
 import com.android.compose.animation.scene.SceneScope
@@ -118,7 +118,7 @@
     statusBarIconController: StatusBarIconController,
     modifier: Modifier = Modifier,
 ) {
-    val isDisabled by viewModel.isDisabled.collectAsState()
+    val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle()
     if (isDisabled) {
         return
     }
@@ -138,7 +138,7 @@
             }
         }
 
-    val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
+    val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
 
     // This layout assumes it is globally positioned at (0, 0) and is the
     // same size as the screen.
@@ -271,7 +271,7 @@
     statusBarIconController: StatusBarIconController,
     modifier: Modifier = Modifier,
 ) {
-    val isDisabled by viewModel.isDisabled.collectAsState()
+    val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle()
     if (isDisabled) {
         return
     }
@@ -280,7 +280,7 @@
         derivedStateOf { shouldUseExpandedFormat(layoutState.transitionState) }
     }
 
-    val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
+    val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
 
     Box(modifier = modifier.sysuiResTag(ShadeHeader.TestTags.Root)) {
         if (isPrivacyChipVisible) {
@@ -435,7 +435,7 @@
     modifier: Modifier = Modifier,
 ) {
     Row(modifier = modifier) {
-        val subIds by viewModel.mobileSubIds.collectAsState()
+        val subIds by viewModel.mobileSubIds.collectAsStateWithLifecycle()
 
         for (subId in subIds) {
             Spacer(modifier = Modifier.width(5.dp))
@@ -472,10 +472,12 @@
     val micSlot = stringResource(id = com.android.internal.R.string.status_bar_microphone)
     val locationSlot = stringResource(id = com.android.internal.R.string.status_bar_location)
 
-    val isSingleCarrier by viewModel.isSingleCarrier.collectAsState()
-    val isPrivacyChipEnabled by viewModel.isPrivacyChipEnabled.collectAsState()
-    val isMicCameraIndicationEnabled by viewModel.isMicCameraIndicationEnabled.collectAsState()
-    val isLocationIndicationEnabled by viewModel.isLocationIndicationEnabled.collectAsState()
+    val isSingleCarrier by viewModel.isSingleCarrier.collectAsStateWithLifecycle()
+    val isPrivacyChipEnabled by viewModel.isPrivacyChipEnabled.collectAsStateWithLifecycle()
+    val isMicCameraIndicationEnabled by
+        viewModel.isMicCameraIndicationEnabled.collectAsStateWithLifecycle()
+    val isLocationIndicationEnabled by
+        viewModel.isLocationIndicationEnabled.collectAsStateWithLifecycle()
 
     AndroidView(
         factory = { context ->
@@ -544,7 +546,7 @@
     viewModel: ShadeHeaderViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val privacyList by viewModel.privacyItems.collectAsState()
+    val privacyList by viewModel.privacyItems.collectAsStateWithLifecycle()
 
     AndroidView(
         factory = { context ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 869f741..a0278a6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -45,7 +45,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -58,6 +57,7 @@
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.colorResource
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexScenePicker
 import com.android.compose.animation.scene.SceneScope
@@ -71,6 +71,7 @@
 import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
 import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
 import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
+import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
@@ -177,7 +178,7 @@
     modifier: Modifier = Modifier,
     shadeSession: SaveableSession,
 ) {
-    val shadeMode by viewModel.shadeMode.collectAsState()
+    val shadeMode by viewModel.shadeMode.collectAsStateWithLifecycle()
     when (shadeMode) {
         is ShadeMode.Single ->
             SingleShade(
@@ -228,8 +229,8 @@
             key = QuickSettings.SharedValues.TilesSquishiness,
             canOverflow = false
         )
-    val isClickable by viewModel.isClickable.collectAsState()
-    val isMediaVisible by viewModel.isMediaVisible.collectAsState()
+    val isClickable by viewModel.isClickable.collectAsStateWithLifecycle()
+    val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
 
     val shouldPunchHoleBehindScrim =
         layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade) ||
@@ -298,6 +299,7 @@
                             stackScrollView = notificationStackScrollView,
                             viewModel = viewModel.notifications,
                             maxScrimTop = { maxNotifScrimTop.value },
+                            shadeMode = ShadeMode.Single,
                             shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
                         )
                     },
@@ -334,10 +336,11 @@
 ) {
     val screenCornerRadius = LocalScreenCornerRadius.current
 
-    val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
-    val isCustomizerShowing by viewModel.qsSceneAdapter.isCustomizerShowing.collectAsState()
+    val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
+    val isCustomizerShowing by
+        viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
     val customizingAnimationDuration by
-        viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsState()
+        viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsStateWithLifecycle()
     val lifecycleOwner = LocalLifecycleOwner.current
     val footerActionsViewModel =
         remember(lifecycleOwner, viewModel) { viewModel.getFooterActionsViewModel(lifecycleOwner) }
@@ -352,13 +355,13 @@
             .unfoldTranslationX(
                 isOnStartSide = true,
             )
-            .collectAsState(0f)
+            .collectAsStateWithLifecycle(0f)
     val unfoldTranslationXForEndSide by
         viewModel
             .unfoldTranslationX(
                 isOnStartSide = false,
             )
-            .collectAsState(0f)
+            .collectAsStateWithLifecycle(0f)
 
     val navBarBottomHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
     val bottomPadding by
@@ -382,7 +385,8 @@
         }
     }
 
-    val brightnessMirrorShowing by viewModel.brightnessMirrorViewModel.isShowing.collectAsState()
+    val brightnessMirrorShowing by
+        viewModel.brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle()
     val contentAlpha by
         animateFloatAsState(
             targetValue = if (brightnessMirrorShowing) 0f else 1f,
@@ -392,7 +396,7 @@
     viewModel.notifications.setAlphaForBrightnessMirror(contentAlpha)
     DisposableEffect(Unit) { onDispose { viewModel.notifications.setAlphaForBrightnessMirror(1f) } }
 
-    val isMediaVisible by viewModel.isMediaVisible.collectAsState()
+    val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
 
     val brightnessMirrorShowingModifier = Modifier.graphicsLayer { alpha = contentAlpha }
 
@@ -443,6 +447,7 @@
                         Column(
                             modifier =
                                 Modifier.fillMaxSize()
+                                    .sysuiResTag("expanded_qs_scroll_view")
                                     .weight(1f)
                                     .thenIf(!isCustomizerShowing) {
                                         Modifier.verticalNestedScrollToScene()
@@ -481,6 +486,7 @@
                             lifecycleOwner = lifecycleOwner,
                             modifier =
                                 Modifier.align(Alignment.CenterHorizontally)
+                                    .sysuiResTag("qs_footer_actions")
                                     .then(brightnessMirrorShowingModifier),
                         )
                     }
@@ -492,6 +498,7 @@
                     viewModel = viewModel.notifications,
                     maxScrimTop = { 0f },
                     shouldPunchHoleBehindScrim = false,
+                    shadeMode = ShadeMode.Split,
                     modifier =
                         Modifier.weight(1f)
                             .fillMaxHeight()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
index 5e107c6..931ff56 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
@@ -3,9 +3,9 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.shadeHeaderText
 import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
 
@@ -14,8 +14,8 @@
     viewModel: ShadeHeaderViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val longerText = viewModel.longerDateText.collectAsState()
-    val shorterText = viewModel.shorterDateText.collectAsState()
+    val longerText = viewModel.longerDateText.collectAsStateWithLifecycle()
+    val shorterText = viewModel.shorterDateText.collectAsStateWithLifecycle()
 
     Layout(
         contents =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
index 79d17ef..3976c61 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
@@ -16,29 +16,40 @@
 
 package com.android.systemui.volume.panel.component.anc.ui.composable
 
+import android.view.Gravity
 import androidx.compose.foundation.basicMarquee
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonColors
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.LiveRegionMode
 import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.semantics.contentDescription
-import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.liveRegion
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.res.R
 import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
+import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup
 import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
 import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
 import javax.inject.Inject
@@ -52,34 +63,47 @@
 
     @Composable
     override fun VolumePanelComposeScope.Content(modifier: Modifier) {
-        val slice by viewModel.buttonSlice.collectAsState()
+        val slice by viewModel.buttonSlice.collectAsStateWithLifecycle()
         val label = stringResource(R.string.volume_panel_noise_control_title)
+        val screenWidth: Float =
+            with(LocalDensity.current) { LocalConfiguration.current.screenWidthDp.dp.toPx() }
+        var gravity by remember { mutableIntStateOf(Gravity.CENTER_HORIZONTAL) }
         val isClickable = viewModel.isClickable(slice)
-        val onClick =
-            if (isClickable) {
-                { ancPopup.show(null) }
-            } else {
-                null
-            }
         Column(
-            modifier = modifier,
+            modifier =
+                modifier.onGloballyPositioned {
+                    gravity = VolumePanelPopup.calculateGravity(it, screenWidth)
+                },
             verticalArrangement = Arrangement.spacedBy(12.dp),
             horizontalAlignment = Alignment.CenterHorizontally,
         ) {
-            SliceAndroidView(
-                modifier =
-                    Modifier.height(64.dp)
-                        .fillMaxWidth()
-                        .semantics {
-                            role = Role.Button
+            Box(
+                modifier = Modifier.height(64.dp),
+            ) {
+                SliceAndroidView(
+                    modifier = modifier.fillMaxSize(),
+                    slice = slice,
+                    onWidthChanged = viewModel::onButtonSliceWidthChanged,
+                    enableAccessibility = false,
+                )
+                Button(
+                    modifier =
+                        modifier.fillMaxSize().padding(8.dp).semantics {
+                            liveRegion = LiveRegionMode.Polite
                             contentDescription = label
-                        }
-                        .clip(RoundedCornerShape(28.dp)),
-                slice = slice,
-                isEnabled = onClick != null,
-                onWidthChanged = viewModel::onButtonSliceWidthChanged,
-                onClick = onClick,
-            )
+                        },
+                    enabled = isClickable,
+                    onClick = { with(ancPopup) { show(null, gravity) } },
+                    colors =
+                        ButtonColors(
+                            contentColor = Color.Transparent,
+                            containerColor = Color.Transparent,
+                            disabledContentColor = Color.Transparent,
+                            disabledContainerColor = Color.Transparent,
+                        )
+                ) {}
+            }
+
             Text(
                 modifier = Modifier.clearAndSetSemantics {}.basicMarquee(),
                 text = label,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
index e1ee01e..15df1be 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
@@ -16,17 +16,18 @@
 
 package com.android.systemui.volume.panel.component.anc.ui.composable
 
+import android.view.Gravity
 import androidx.compose.foundation.basicMarquee
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.slice.Slice
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.animation.Expandable
@@ -47,9 +48,10 @@
 ) {
 
     /** Shows a popup with the [expandable] animation. */
-    fun show(expandable: Expandable?) {
+    fun show(expandable: Expandable?, horizontalGravity: Int) {
         uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_ANC_POPUP_SHOWN)
-        volumePanelPopup.show(expandable, { Title() }, { Content(it) })
+        val gravity = horizontalGravity or Gravity.BOTTOM
+        volumePanelPopup.show(expandable, gravity, { Title() }, { Content(it) })
     }
 
     @Composable
@@ -65,14 +67,14 @@
 
     @Composable
     private fun Content(dialog: SystemUIDialog) {
-        val isAvailable by viewModel.isAvailable.collectAsState(true)
+        val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle(true)
 
         if (!isAvailable) {
             SideEffect { dialog.dismiss() }
             return
         }
 
-        val slice by viewModel.popupSlice.collectAsState()
+        val slice by viewModel.popupSlice.collectAsStateWithLifecycle()
         SliceAndroidView(
             modifier = Modifier.fillMaxWidth(),
             slice = slice,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
index fc5d212..23d50c5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
@@ -16,11 +16,12 @@
 
 package com.android.systemui.volume.panel.component.anc.ui.composable
 
-import android.annotation.SuppressLint
 import android.content.Context
+import android.os.Bundle
 import android.view.ContextThemeWrapper
-import android.view.MotionEvent
 import android.view.View
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.viewinterop.AndroidView
@@ -32,14 +33,13 @@
 fun SliceAndroidView(
     slice: Slice?,
     modifier: Modifier = Modifier,
-    isEnabled: Boolean = true,
     onWidthChanged: ((Int) -> Unit)? = null,
-    onClick: (() -> Unit)? = null,
+    enableAccessibility: Boolean = true,
 ) {
     AndroidView(
         modifier = modifier,
         factory = { context: Context ->
-            ClickableSliceView(
+            ComposeSliceView(
                     ContextThemeWrapper(context, R.style.Widget_SliceView_VolumePanel),
                 )
                 .apply {
@@ -47,17 +47,18 @@
                     isScrollable = false
                     importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
                     setShowTitleItems(true)
-                    if (onWidthChanged != null) {
-                        addOnLayoutChangeListener(OnWidthChangedLayoutListener(onWidthChanged))
-                    }
                 }
         },
-        update = { sliceView: ClickableSliceView ->
+        update = { sliceView: ComposeSliceView ->
             sliceView.slice = slice
-            sliceView.onClick = onClick
-            sliceView.isEnabled = isEnabled
-            sliceView.isClickable = isEnabled
-        }
+            sliceView.layoutListener = onWidthChanged?.let(::OnWidthChangedLayoutListener)
+            sliceView.enableAccessibility = enableAccessibility
+        },
+        onRelease = { sliceView: ComposeSliceView ->
+            sliceView.layoutListener = null
+            sliceView.slice = null
+            sliceView.enableAccessibility = true
+        },
     )
 }
 
@@ -83,26 +84,39 @@
     }
 }
 
-/**
- * [SliceView] that prioritises [onClick] when its clicked instead of passing the event to the slice
- * first.
- */
-@SuppressLint("ViewConstructor") // only used in this class
-private class ClickableSliceView(context: Context) : SliceView(context) {
+private class ComposeSliceView(context: Context) : SliceView(context) {
 
-    var onClick: (() -> Unit)? = null
+    var enableAccessibility: Boolean = true
+    var layoutListener: OnLayoutChangeListener? = null
+        set(value) {
+            field?.let { removeOnLayoutChangeListener(it) }
+            field = value
+            field?.let { addOnLayoutChangeListener(it) }
+        }
 
-    init {
-        if (onClick != null) {
-            setOnClickListener {}
+    override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
+        if (enableAccessibility) {
+            super.onInitializeAccessibilityNodeInfo(info)
         }
     }
 
-    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
-        return (isSliceViewClickable && onClick != null) || super.onInterceptTouchEvent(ev)
+    override fun onInitializeAccessibilityEvent(event: AccessibilityEvent?) {
+        if (enableAccessibility) {
+            super.onInitializeAccessibilityEvent(event)
+        }
     }
 
-    override fun onClick(v: View?) {
-        onClick?.takeIf { isSliceViewClickable }?.let { it() } ?: super.onClick(v)
+    override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
+        return if (enableAccessibility) {
+            super.performAccessibilityAction(action, arguments)
+        } else {
+            false
+        }
+    }
+
+    override fun addChildrenForAccessibility(outChildren: ArrayList<View>?) {
+        if (enableAccessibility) {
+            super.addChildrenForAccessibility(outChildren)
+        }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
index 0893b9d..e1ae80f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.panel.component.button.ui.composable
 
+import android.view.Gravity
 import androidx.compose.foundation.basicMarquee
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
@@ -27,20 +28,27 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Expandable
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
+import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup.Companion.calculateGravity
 import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
 import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
 import kotlinx.coroutines.flow.StateFlow
@@ -48,17 +56,22 @@
 /** [ComposeVolumePanelUiComponent] implementing a clickable button from a bottom row. */
 class ButtonComponent(
     private val viewModelFlow: StateFlow<ButtonViewModel?>,
-    private val onClick: (Expandable) -> Unit
+    private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit
 ) : ComposeVolumePanelUiComponent {
 
     @Composable
     override fun VolumePanelComposeScope.Content(modifier: Modifier) {
-        val viewModelByState by viewModelFlow.collectAsState()
+        val viewModelByState by viewModelFlow.collectAsStateWithLifecycle()
         val viewModel = viewModelByState ?: return
         val label = viewModel.label.toString()
 
+        val screenWidth: Float =
+            with(LocalDensity.current) { LocalConfiguration.current.screenWidthDp.dp.toPx() }
+        var gravity by remember { mutableIntStateOf(Gravity.CENTER_HORIZONTAL) }
+
         Column(
-            modifier = modifier,
+            modifier =
+                modifier.onGloballyPositioned { gravity = calculateGravity(it, screenWidth) },
             verticalArrangement = Arrangement.spacedBy(12.dp),
             horizontalAlignment = Alignment.CenterHorizontally,
         ) {
@@ -82,7 +95,7 @@
                         } else {
                             MaterialTheme.colorScheme.onSurface
                         },
-                    onClick = onClick,
+                    onClick = { onClick(it, gravity) },
                 ) {
                     Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                         Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index 12debbc..1b821d3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -29,7 +29,6 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -42,6 +41,7 @@
 import androidx.compose.ui.semantics.toggleableState
 import androidx.compose.ui.state.ToggleableState
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
 import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
@@ -56,7 +56,7 @@
 
     @Composable
     override fun VolumePanelComposeScope.Content(modifier: Modifier) {
-        val viewModelByState by viewModelFlow.collectAsState()
+        val viewModelByState by viewModelFlow.collectAsStateWithLifecycle()
         val viewModel = viewModelByState ?: return
         val label = viewModel.label.toString()
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
index ded63a1..237bbfd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
@@ -45,7 +45,6 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -55,6 +54,7 @@
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Expandable
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.common.ui.compose.toColor
@@ -77,9 +77,9 @@
     @Composable
     override fun VolumePanelComposeScope.Content(modifier: Modifier) {
         val connectedDeviceViewModel: ConnectedDeviceViewModel? by
-            viewModel.connectedDeviceViewModel.collectAsState()
+            viewModel.connectedDeviceViewModel.collectAsStateWithLifecycle()
         val deviceIconViewModel: DeviceIconViewModel? by
-            viewModel.deviceIconViewModel.collectAsState()
+            viewModel.deviceIconViewModel.collectAsStateWithLifecycle()
         val clickLabel = stringResource(R.string.volume_panel_enter_media_output_settings)
 
         Expandable(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
index bb4e957..3b1bf2a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.volume.panel.component.popup.ui.composable
 
 import android.view.Gravity
+import androidx.annotation.GravityInt
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -31,6 +32,8 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.boundsInRoot
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.semantics.paneTitle
@@ -60,13 +63,14 @@
      */
     fun show(
         expandable: Expandable?,
+        @GravityInt gravity: Int,
         title: @Composable (SystemUIDialog) -> Unit,
         content: @Composable (SystemUIDialog) -> Unit,
     ) {
         val dialog =
             dialogFactory.create(
                 theme = R.style.Theme_VolumePanel_Popup,
-                dialogGravity = Gravity.BOTTOM,
+                dialogGravity = gravity,
             ) {
                 PopupComposable(it, title, content)
             }
@@ -122,4 +126,23 @@
             }
         }
     }
+
+    companion object {
+
+        /**
+         * Returns absolute ([Gravity.LEFT], [Gravity.RIGHT] or [Gravity.CENTER_HORIZONTAL])
+         * [GravityInt] for the popup based on the [coordinates] global position relative to the
+         * [screenWidthPx].
+         */
+        @GravityInt
+        fun calculateGravity(coordinates: LayoutCoordinates, screenWidthPx: Float): Int {
+            val bottomCenter: Float = coordinates.boundsInRoot().bottomCenter.x
+            val rootBottomCenter: Float = screenWidthPx / 2
+            return when {
+                bottomCenter < rootBottomCenter -> Gravity.LEFT
+                bottomCenter > rootBottomCenter -> Gravity.RIGHT
+                else -> Gravity.CENTER_HORIZONTAL
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index 12d2bc2..9891b5b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -16,17 +16,18 @@
 
 package com.android.systemui.volume.panel.component.spatialaudio.ui.composable
 
+import android.view.Gravity
 import androidx.compose.foundation.basicMarquee
 import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.ui.compose.Icon
@@ -47,14 +48,15 @@
 ) {
 
     /** Shows a popup with the [expandable] animation. */
-    fun show(expandable: Expandable) {
+    fun show(expandable: Expandable, horizontalGravity: Int) {
         uiEventLogger.logWithPosition(
             VolumePanelUiEvent.VOLUME_PANEL_SPATIAL_AUDIO_POP_UP_SHOWN,
             0,
             null,
             viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isActive }
         )
-        volumePanelPopup.show(expandable, { Title() }, { Content(it) })
+        val gravity = horizontalGravity or Gravity.BOTTOM
+        volumePanelPopup.show(expandable, gravity, { Title() }, { Content(it) })
     }
 
     @Composable
@@ -70,14 +72,14 @@
 
     @Composable
     private fun Content(dialog: SystemUIDialog) {
-        val isAvailable by viewModel.isAvailable.collectAsState()
+        val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle()
 
         if (!isAvailable) {
             SideEffect { dialog.dismiss() }
             return
         }
 
-        val enabledModelStates by viewModel.spatialAudioButtons.collectAsState()
+        val enabledModelStates by viewModel.spatialAudioButtons.collectAsStateWithLifecycle()
         if (enabledModelStates.isEmpty()) {
             return
         }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
index a3467f2..072e91a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -20,6 +20,8 @@
 import androidx.compose.animation.EnterTransition
 import androidx.compose.animation.ExitTransition
 import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.core.tween
 import androidx.compose.animation.core.updateTransition
 import androidx.compose.animation.expandVertically
@@ -28,10 +30,8 @@
 import androidx.compose.animation.scaleIn
 import androidx.compose.animation.scaleOut
 import androidx.compose.animation.shrinkVertically
-import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
@@ -39,7 +39,7 @@
 import androidx.compose.material3.IconButton
 import androidx.compose.material3.IconButtonDefaults
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -49,15 +49,22 @@
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformSliderColors
+import com.android.compose.modifiers.padding
 import com.android.systemui.res.R
 import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
 
 private const val EXPAND_DURATION_MILLIS = 500
+private const val COLLAPSE_EXPAND_BUTTON_DELAY_MILLIS = 350
 private const val COLLAPSE_DURATION_MILLIS = 300
+private const val EXPAND_BUTTON_ANIMATION_DURATION_MILLIS = 350
+private const val TOP_SLIDER_ANIMATION_DURATION_MILLIS = 400
 private const val SHRINK_FRACTION = 0.55f
 private const val SCALE_FRACTION = 0.9f
+private const val EXPAND_BUTTON_SCALE = 0.8f
 
 /** Volume sliders laid out in a collapsable column */
 @OptIn(ExperimentalAnimationApi::class)
@@ -73,14 +80,15 @@
     require(viewModels.isNotEmpty())
     val transition = updateTransition(isExpanded, label = "CollapsableSliders")
     Column(modifier = modifier) {
-        Row(
+        Box(
             modifier = Modifier.fillMaxWidth(),
-            horizontalArrangement = Arrangement.spacedBy(8.dp),
         ) {
             val sliderViewModel: SliderViewModel = viewModels.first()
-            val sliderState by viewModels.first().slider.collectAsState()
+            val sliderState by viewModels.first().slider.collectAsStateWithLifecycle()
+            val sliderPadding by topSliderPadding(isExpandable)
+
             VolumeSlider(
-                modifier = Modifier.weight(1f),
+                modifier = Modifier.padding(end = { sliderPadding.roundToPx() }).fillMaxWidth(),
                 state = sliderState,
                 onValueChange = { newValue: Float ->
                     sliderViewModel.onValueChanged(sliderState, newValue)
@@ -90,21 +98,13 @@
                 sliderColors = sliderColors,
             )
 
-            val expandButtonStateDescription =
-                if (isExpanded) stringResource(R.string.volume_panel_expanded_sliders)
-                else stringResource(R.string.volume_panel_collapsed_sliders)
-            if (isExpandable) {
-                ExpandButton(
-                    modifier =
-                        Modifier.semantics {
-                            role = Role.Switch
-                            stateDescription = expandButtonStateDescription
-                        },
-                    isExpanded = isExpanded,
-                    onExpandedChanged = onExpandedChanged,
-                    sliderColors = sliderColors,
-                )
-            }
+            ExpandButton(
+                modifier = Modifier.align(Alignment.CenterEnd),
+                isExpanded = isExpanded,
+                isExpandable = isExpandable,
+                onExpandedChanged = onExpandedChanged,
+                sliderColors = sliderColors,
+            )
         }
         transition.AnimatedVisibility(
             visible = { it || !isExpandable },
@@ -119,7 +119,7 @@
                 Column {
                     for (index in 1..viewModels.lastIndex) {
                         val sliderViewModel: SliderViewModel = viewModels[index]
-                        val sliderState by sliderViewModel.slider.collectAsState()
+                        val sliderState by sliderViewModel.slider.collectAsStateWithLifecycle()
                         transition.AnimatedVisibility(
                             modifier = Modifier.padding(top = 16.dp),
                             visible = { it || !isExpandable },
@@ -147,30 +147,48 @@
 @Composable
 private fun ExpandButton(
     isExpanded: Boolean,
+    isExpandable: Boolean,
     onExpandedChanged: (Boolean) -> Unit,
     sliderColors: PlatformSliderColors,
     modifier: Modifier = Modifier,
 ) {
-    IconButton(
-        modifier = modifier.size(64.dp),
-        onClick = { onExpandedChanged(!isExpanded) },
-        colors =
-            IconButtonDefaults.filledIconButtonColors(
-                containerColor = sliderColors.indicatorColor,
-                contentColor = sliderColors.iconColor
-            ),
+    val expandButtonStateDescription =
+        if (isExpanded) {
+            stringResource(R.string.volume_panel_expanded_sliders)
+        } else {
+            stringResource(R.string.volume_panel_collapsed_sliders)
+        }
+    AnimatedVisibility(
+        modifier = modifier,
+        visible = isExpandable,
+        enter = expandButtonEnterTransition(),
+        exit = expandButtonExitTransition(),
     ) {
-        Icon(
-            painter =
-                painterResource(
-                    if (isExpanded) {
-                        R.drawable.ic_filled_arrow_down
-                    } else {
-                        R.drawable.ic_filled_arrow_up
-                    }
+        IconButton(
+            modifier =
+                Modifier.size(64.dp).semantics {
+                    role = Role.Switch
+                    stateDescription = expandButtonStateDescription
+                },
+            onClick = { onExpandedChanged(!isExpanded) },
+            colors =
+                IconButtonDefaults.filledIconButtonColors(
+                    containerColor = sliderColors.indicatorColor,
+                    contentColor = sliderColors.iconColor
                 ),
-            contentDescription = null,
-        )
+        ) {
+            Icon(
+                painter =
+                    painterResource(
+                        if (isExpanded) {
+                            R.drawable.ic_filled_arrow_down
+                        } else {
+                            R.drawable.ic_filled_arrow_up
+                        }
+                    ),
+                contentDescription = null,
+            )
+        }
     }
 }
 
@@ -204,3 +222,63 @@
         ) +
         fadeOut(animationSpec = tween(durationMillis = exitDuration))
 }
+
+private fun expandButtonEnterTransition(): EnterTransition {
+    return fadeIn(
+        tween(
+            delayMillis = COLLAPSE_EXPAND_BUTTON_DELAY_MILLIS,
+            durationMillis = EXPAND_BUTTON_ANIMATION_DURATION_MILLIS,
+        )
+    ) +
+        scaleIn(
+            animationSpec =
+                tween(
+                    delayMillis = COLLAPSE_EXPAND_BUTTON_DELAY_MILLIS,
+                    durationMillis = EXPAND_BUTTON_ANIMATION_DURATION_MILLIS,
+                ),
+            initialScale = EXPAND_BUTTON_SCALE,
+        )
+}
+
+private fun expandButtonExitTransition(): ExitTransition {
+    return fadeOut(
+        tween(
+            delayMillis = EXPAND_DURATION_MILLIS,
+            durationMillis = EXPAND_BUTTON_ANIMATION_DURATION_MILLIS,
+        )
+    ) +
+        scaleOut(
+            animationSpec =
+                tween(
+                    delayMillis = EXPAND_DURATION_MILLIS,
+                    durationMillis = EXPAND_BUTTON_ANIMATION_DURATION_MILLIS,
+                ),
+            targetScale = EXPAND_BUTTON_SCALE,
+        )
+}
+
+@Composable
+private fun topSliderPadding(isExpandable: Boolean): State<Dp> {
+    val animationSpec: AnimationSpec<Dp> =
+        if (isExpandable) {
+            tween(
+                delayMillis = COLLAPSE_DURATION_MILLIS,
+                durationMillis = TOP_SLIDER_ANIMATION_DURATION_MILLIS,
+            )
+        } else {
+            tween(
+                delayMillis = EXPAND_DURATION_MILLIS,
+                durationMillis = TOP_SLIDER_ANIMATION_DURATION_MILLIS,
+            )
+        }
+    return animateDpAsState(
+        targetValue =
+            if (isExpandable) {
+                72.dp
+            } else {
+                0.dp
+            },
+        animationSpec = animationSpec,
+        label = "TopVolumeSliderPadding"
+    )
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
index bb17499..d15430f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
@@ -18,9 +18,9 @@
 
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformSliderColors
 import com.android.compose.grid.VerticalGrid
 import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
@@ -39,7 +39,7 @@
         horizontalSpacing = 24.dp,
     ) {
         for (sliderViewModel in viewModels) {
-            val sliderState = sliderViewModel.slider.collectAsState().value
+            val sliderState = sliderViewModel.slider.collectAsStateWithLifecycle().value
             VolumeSlider(
                 modifier = Modifier.fillMaxWidth(),
                 state = sliderState,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index cb3867f..271eb96 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -78,12 +78,7 @@
                     }
 
                     state.a11yStateDescription?.let { stateDescription = it }
-                        ?: run {
-                            // provide a not animated value to the a11y because it fails to announce
-                            // the settled value when it changes rapidly.
-                            progressBarRangeInfo =
-                                ProgressBarRangeInfo(state.value, state.valueRange)
-                        }
+                    progressBarRangeInfo = ProgressBarRangeInfo(state.value, state.valueRange)
                 } else {
                     disabled()
                     contentDescription =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
index fdf8ee8..770c5d5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
@@ -18,12 +18,13 @@
 
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformSliderDefaults
 import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
 import com.android.systemui.volume.panel.component.volume.ui.viewmodel.AudioVolumeComponentViewModel
+import com.android.systemui.volume.panel.component.volume.ui.viewmodel.SlidersExpandableViewModel
 import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
 import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
 import com.android.systemui.volume.panel.ui.composable.isPortrait
@@ -37,7 +38,8 @@
 
     @Composable
     override fun VolumePanelComposeScope.Content(modifier: Modifier) {
-        val sliderViewModels: List<SliderViewModel> by viewModel.sliderViewModels.collectAsState()
+        val sliderViewModels: List<SliderViewModel> by
+            viewModel.sliderViewModels.collectAsStateWithLifecycle()
         if (sliderViewModels.isEmpty()) {
             return
         }
@@ -48,13 +50,21 @@
                 modifier = modifier.fillMaxWidth(),
             )
         } else {
-            val isExpanded by viewModel.isExpanded.collectAsState()
+            val expandableViewModel: SlidersExpandableViewModel by
+                viewModel
+                    .isExpandable(isPortrait)
+                    .collectAsStateWithLifecycle(SlidersExpandableViewModel.Unavailable)
+            if (expandableViewModel is SlidersExpandableViewModel.Unavailable) {
+                return
+            }
+            val isExpanded =
+                (expandableViewModel as? SlidersExpandableViewModel.Expandable)?.isExpanded ?: true
             ColumnVolumeSliders(
                 viewModels = sliderViewModels,
                 isExpanded = isExpanded,
                 onExpandedChanged = viewModel::onExpandedChanged,
                 sliderColors = PlatformSliderDefaults.defaultPlatformSliderColors(),
-                isExpandable = isPortrait,
+                isExpandable = expandableViewModel is SlidersExpandableViewModel.Expandable,
                 modifier = modifier.fillMaxWidth(),
             )
         }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index a602e25..83b8158 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -24,7 +24,6 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -32,6 +31,7 @@
 import androidx.compose.ui.semantics.paneTitle
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.res.R
 import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
 import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState
@@ -54,8 +54,8 @@
     }
 
     val accessibilityTitle = stringResource(R.string.accessibility_volume_settings)
-    val state: VolumePanelState by viewModel.volumePanelState.collectAsState()
-    val components by viewModel.componentsLayout.collectAsState(null)
+    val state: VolumePanelState by viewModel.volumePanelState.collectAsStateWithLifecycle()
+    val components by viewModel.componentsLayout.collectAsStateWithLifecycle(null)
 
     with(VolumePanelComposeScope(state)) {
         components?.let { componentsState ->
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index d924d88..92d5c26 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -74,6 +74,16 @@
          */
         val isUserInputOngoing: Flow<Boolean>,
     ) : ObservableTransitionState
+
+    fun isIdle(scene: SceneKey?): Boolean {
+        return this is Idle && (scene == null || this.currentScene == scene)
+    }
+
+    fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean {
+        return this is Transition &&
+            (from == null || this.fromScene == from) &&
+            (to == null || this.toScene == to)
+    }
 }
 
 /**
diff --git a/packages/SystemUI/flag_check.py b/packages/SystemUI/flag_check.py
index bac3553..95a25c5 100755
--- a/packages/SystemUI/flag_check.py
+++ b/packages/SystemUI/flag_check.py
@@ -12,19 +12,20 @@
     %s
 
 The Flag: stanza is regex matched and should describe whether your change is behind a flag or flags.
-
-As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the
-flag in addition to its state (ENABLED|DISABLED|DEVELOPMENT|STAGING|TEAMFOOD|TRUNKFOOD|NEXTFOOD).
+As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the flag.
+For legacy flags use EXEMPT with your flag name.
 
 Some examples below:
 
-Flag: NONE
-Flag: NA
-Flag: LEGACY ENABLE_ONE_SEARCH DISABLED
-Flag: ACONFIG com.android.launcher3.enable_twoline_allapps DEVELOPMENT
-Flag: ACONFIG com.android.launcher3.enable_twoline_allapps TRUNKFOOD
+Flag: NONE Repohook Update
+Flag: TEST_ONLY
+Flag: EXEMPT resource only update
+Flag: EXEMPT bugfix
+Flag: EXEMPT refactor
+Flag: com.android.launcher3.enable_twoline_allapps
+Flag: com.google.android.apps.nexuslauncher.zero_state_web_data_loader
 
-Check the git history for more examples. It's a regex matched field.
+Check the git history for more examples. It's a regex matched field. See go/android-flag-directive for more details on various formats.
 """
 
 def main():
@@ -63,28 +64,31 @@
         return
 
     field = 'Flag'
-    none = '(NONE|NA|N\/A)' # NONE|NA|N/A
+    none = 'NONE'
+    testOnly = 'TEST_ONLY'
+    docsOnly = 'DOCS_ONLY'
+    exempt = 'EXEMPT'
+    justification = '<justification>'
 
-    typeExpression = '\s*(LEGACY|ACONFIG)' # [type:LEGACY|ACONFIG]
-
-    # legacyFlagName contains only uppercase alphabets with '_' - Ex: ENABLE_ONE_SEARCH
-    # Aconfig Flag name format = "packageName"."flagName"
+    # Aconfig Flag name format = <packageName>.<flagName>
     # package name - Contains only lowercase alphabets + digits + '.' - Ex: com.android.launcher3
-    # For now alphabets, digits, "_", "." characters are allowed in flag name and not adding stricter format check.
+    # For now alphabets, digits, "_", "." characters are allowed in flag name.
+    # Checks if there is "one dot" between packageName and flagName and not adding stricter format check
     #common_typos_disable
-    flagName = '([a-zA-z0-9_.])+'
+    flagName = '([a-zA-Z0-9.]+)([.]+)([a-zA-Z0-9_.]+)'
 
-    #[state:ENABLED|DISABLED|DEVELOPMENT|TEAM*(TEAMFOOD)|STAGING|TRUNK*(TRUNK_STAGING, TRUNK_FOOD)|NEXT*(NEXTFOOD)]
-    stateExpression = '\s*(ENABLED|DISABLED|DEVELOPMENT|TEAM[a-zA-z]*|STAGING|TRUNK[a-zA-z]*|NEXT[a-zA-z]*)'
+    # None and Exempt needs justification
+    exemptRegex = fr'{exempt}\s*[a-zA-Z]+'
+    noneRegex = fr'{none}\s*[a-zA-Z]+'
     #common_typos_enable
 
-    readableRegexMsg = '\n\tFlag: (NONE|NA)\n\tFlag: LEGACY|ACONFIG FlagName|packageName.flagName ENABLED|DISABLED|DEVELOPMENT|TEAMFOOD|STAGING|TRUNKFOOD|NEXTFOOD'
+    readableRegexMsg = '\n\tFlag: '+none+' '+justification+'\n\tFlag: <packageName>.<flagName>\n\tFlag: ' +exempt+' '+justification+'\n\tFlag: '+testOnly+'\n\tFlag: '+docsOnly
 
     flagRegex = fr'^{field}: .*$'
     check_flag = re.compile(flagRegex) #Flag:
 
     # Ignore case for flag name format.
-    flagNameRegex = fr'(?i)^{field}:\s*({none}|{typeExpression}\s*{flagName}\s*{stateExpression})\s*'
+    flagNameRegex = fr'(?i)^{field}:\s*({noneRegex}|{flagName}|{testOnly}|{docsOnly}|{exemptRegex})\s*'
     check_flagName = re.compile(flagNameRegex) #Flag: <flag name format>
 
     flagError = False
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 47a00f4..624f18d 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -133,7 +133,7 @@
         @ColorInt seed: Int,
         darkTheme: Boolean,
         style: Style
-    ) : this(seed, darkTheme, style, 0.5)
+    ) : this(seed, darkTheme, style, 0.0)
 
     @JvmOverloads
     constructor(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
index 11a4241..27bffd0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
@@ -18,10 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.view.GestureDetector;
 import android.view.MotionEvent;
@@ -30,6 +28,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
@@ -37,7 +36,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -51,89 +49,66 @@
     CentralSurfaces mCentralSurfaces;
 
     @Mock
+    ShadeViewController mShadeViewController;
+
+    @Mock
     TouchHandler.TouchSession mTouchSession;
 
     ShadeTouchHandler mTouchHandler;
 
-    @Captor
-    ArgumentCaptor<GestureDetector.OnGestureListener> mGestureListenerCaptor;
-    @Captor
-    ArgumentCaptor<InputChannelCompat.InputEventListener> mInputListenerCaptor;
-
     private static final int TOUCH_HEIGHT = 20;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-
-        mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), TOUCH_HEIGHT);
-    }
-
-    // Verifies that a swipe down in the gesture region is captured by the shade touch handler.
-    @Test
-    public void testSwipeDown_captured() {
-        final boolean captured = swipe(Direction.DOWN);
-
-        assertThat(captured).isTrue();
-    }
-
-    // Verifies that a swipe in the upward direction is not catpured.
-    @Test
-    public void testSwipeUp_notCaptured() {
-        final boolean captured = swipe(Direction.UP);
-
-        // Motion events not captured as the swipe is going in the wrong direction.
-        assertThat(captured).isFalse();
-    }
-
-    // Verifies that a swipe down forwards captured touches to the shade window for handling.
-    @Test
-    public void testSwipeDown_sentToShadeWindow() {
-        swipe(Direction.DOWN);
-
-        // Both motion events are sent for the shade window to process.
-        verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any());
-    }
-
-    // Verifies that a swipe down is not forwarded to the shade window.
-    @Test
-    public void testSwipeUp_touchesNotSent() {
-        swipe(Direction.UP);
-
-        // Motion events are not sent for the shade window to process as the swipe is going in the
-        // wrong direction.
-        verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any());
+        mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), mShadeViewController,
+                TOUCH_HEIGHT);
     }
 
     /**
-     * Simulates a swipe in the given direction and returns true if the touch was intercepted by the
-     * touch handler's gesture listener.
-     * <p>
-     * Swipe down starts from a Y coordinate of 0 and goes downward. Swipe up starts from the edge
-     * of the gesture region, {@link #TOUCH_HEIGHT}, and goes upward to 0.
+     * Verify that touches aren't handled when the bouncer is showing.
      */
-    private boolean swipe(Direction direction) {
-        Mockito.clearInvocations(mTouchSession);
+    @Test
+    public void testInactiveOnBouncer() {
+        when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
         mTouchHandler.onSessionStart(mTouchSession);
-
-        verify(mTouchSession).registerGestureListener(mGestureListenerCaptor.capture());
-        verify(mTouchSession).registerInputListener(mInputListenerCaptor.capture());
-
-        final float startY = direction == Direction.UP ? TOUCH_HEIGHT : 0;
-        final float endY = direction == Direction.UP ? 0 : TOUCH_HEIGHT;
-
-        // Send touches to the input and gesture listener.
-        final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, startY, 0);
-        final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, endY, 0);
-        mInputListenerCaptor.getValue().onInputEvent(event1);
-        mInputListenerCaptor.getValue().onInputEvent(event2);
-        final boolean captured = mGestureListenerCaptor.getValue().onScroll(event1, event2, 0,
-                startY - endY);
-
-        return captured;
+        verify(mTouchSession).pop();
     }
 
-    private enum Direction {
-        DOWN, UP,
+    /**
+     * Make sure {@link ShadeTouchHandler}
+     */
+    @Test
+    public void testTouchPilferingOnScroll() {
+        final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class);
+        final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class);
+
+        final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerArgumentCaptor =
+                ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+
+        mTouchHandler.onSessionStart(mTouchSession);
+        verify(mTouchSession).registerGestureListener(gestureListenerArgumentCaptor.capture());
+
+        assertThat(gestureListenerArgumentCaptor.getValue()
+                .onScroll(motionEvent1, motionEvent2, 1, 1))
+                .isTrue();
     }
+
+    /**
+     * Ensure touches are propagated to the {@link ShadeViewController}.
+     */
+    @Test
+    public void testEventPropagation() {
+        final MotionEvent motionEvent = Mockito.mock(MotionEvent.class);
+
+        final ArgumentCaptor<InputChannelCompat.InputEventListener>
+                inputEventListenerArgumentCaptor =
+                    ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+
+        mTouchHandler.onSessionStart(mTouchSession);
+        verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
+        inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
+        verify(mShadeViewController).handleExternalTouch(motionEvent);
+    }
+
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index f4ad764..9b1d4ec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -84,7 +84,6 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
-import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
@@ -161,8 +160,6 @@
     @Mock
     private InteractionJankMonitor mInteractionJankMonitor;
     @Mock
-    private PromptCredentialInteractor mBiometricPromptCredentialInteractor;
-    @Mock
     private PromptSelectorInteractor mPromptSelectionInteractor;
     @Mock
     private CredentialViewModel mCredentialViewModel;
@@ -1057,7 +1054,6 @@
 
     private final class TestableAuthController extends AuthController {
         private int mBuildCount = 0;
-        private PromptInfo mLastBiometricPromptInfo;
 
         TestableAuthController(Context context) {
             super(context, null /* applicationCoroutineScope */,
@@ -1065,8 +1061,8 @@
                     mFingerprintManager, mFaceManager, () -> mUdfpsController, mDisplayManager,
                     mWakefulnessLifecycle, mPanelInteractionDetector, mUserManager,
                     mLockPatternUtils, () -> mUdfpsLogger, () -> mLogContextInteractor,
-                    () -> mBiometricPromptCredentialInteractor, () -> mPromptSelectionInteractor,
-                    () -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor,
+                    () -> mPromptSelectionInteractor, () -> mCredentialViewModel,
+                    () -> mPromptViewModel, mInteractionJankMonitor,
                     mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper);
         }
 
@@ -1079,8 +1075,6 @@
                 UserManager userManager,
                 LockPatternUtils lockPatternUtils, PromptViewModel viewModel) {
 
-            mLastBiometricPromptInfo = promptInfo;
-
             AuthDialog dialog;
             if (mBuildCount == 0) {
                 dialog = mDialog1;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index ecfcc90..a5acf72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -66,15 +66,12 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
-    private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
-    private val sceneContainerStartable = kosmos.sceneContainerStartable
 
     private lateinit var underTest: BouncerViewModel
 
     @Before
     fun setUp() {
-        sceneContainerStartable.start()
+        kosmos.sceneContainerStartable.start()
         underTest = kosmos.bouncerViewModel
     }
 
@@ -164,11 +161,11 @@
             assertThat(isInputEnabled).isTrue()
 
             repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
-                bouncerInteractor.authenticate(WRONG_PIN)
+                kosmos.bouncerInteractor.authenticate(WRONG_PIN)
             }
             assertThat(isInputEnabled).isFalse()
 
-            val lockoutEndMs = authenticationInteractor.lockoutEndTimestamp ?: 0
+            val lockoutEndMs = kosmos.authenticationInteractor.lockoutEndTimestamp ?: 0
             advanceTimeBy(lockoutEndMs - testScope.currentTime)
             assertThat(isInputEnabled).isTrue()
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt
index 312c14d..fec56ed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt
@@ -18,9 +18,13 @@
 
 import android.content.applicationContext
 import android.os.UserManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
 import androidx.test.filters.SmallTest
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
+import com.android.systemui.Flags.FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testDispatcher
@@ -40,15 +44,19 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class BrightnessPolicyRepositoryImplTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class BrightnessPolicyRepositoryImplTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
 
     private val kosmos = testKosmos()
 
-    private val fakeUserRepository = kosmos.fakeUserRepository
-
     private val mockUserRestrictionChecker: UserRestrictionChecker = mock {
         whenever(checkIfRestrictionEnforced(any(), anyString(), anyInt())).thenReturn(null)
         whenever(hasBaseUserRestriction(any(), anyString(), anyInt())).thenReturn(false)
@@ -130,7 +138,83 @@
             }
         }
 
-    private companion object {
-        val RESTRICTION = UserManager.DISALLOW_CONFIG_BRIGHTNESS
+    @Test
+    @DisableFlags(FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION)
+    fun brightnessBaseUserRestriction_flagOff_noRestriction() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(
+                        mockUserRestrictionChecker.hasBaseUserRestriction(
+                            any(),
+                            eq(RESTRICTION),
+                            eq(userRepository.getSelectedUserInfo().id)
+                        )
+                    )
+                    .thenReturn(true)
+
+                val restrictions by collectLastValue(underTest.restrictionPolicy)
+
+                assertThat(restrictions).isEqualTo(PolicyRestriction.NoRestriction)
+            }
+        }
+
+    @Test
+    fun bothRestrictions_returnsSetEnforcedAdminFromCheck() =
+        with(kosmos) {
+            testScope.runTest {
+                val enforcedAdmin: EnforcedAdmin =
+                    EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(RESTRICTION)
+
+                whenever(
+                        mockUserRestrictionChecker.checkIfRestrictionEnforced(
+                            any(),
+                            eq(RESTRICTION),
+                            eq(userRepository.getSelectedUserInfo().id)
+                        )
+                    )
+                    .thenReturn(enforcedAdmin)
+
+                whenever(
+                        mockUserRestrictionChecker.hasBaseUserRestriction(
+                            any(),
+                            eq(RESTRICTION),
+                            eq(userRepository.getSelectedUserInfo().id)
+                        )
+                    )
+                    .thenReturn(true)
+
+                val restrictions by collectLastValue(underTest.restrictionPolicy)
+
+                assertThat(restrictions).isEqualTo(PolicyRestriction.Restricted(enforcedAdmin))
+            }
+        }
+
+    @Test
+    @EnableFlags(FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION)
+    fun brightnessBaseUserRestriction_flagOn_emptyRestriction() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(
+                        mockUserRestrictionChecker.hasBaseUserRestriction(
+                            any(),
+                            eq(RESTRICTION),
+                            eq(userRepository.getSelectedUserInfo().id)
+                        )
+                    )
+                    .thenReturn(true)
+
+                val restrictions by collectLastValue(underTest.restrictionPolicy)
+
+                assertThat(restrictions).isEqualTo(PolicyRestriction.Restricted(EnforcedAdmin()))
+            }
+        }
+
+    companion object {
+        private const val RESTRICTION = UserManager.DISALLOW_CONFIG_BRIGHTNESS
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return allCombinationsOf(FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION)
+        }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt
index 85a4bcf..11f5238 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt
@@ -48,7 +48,6 @@
     private val kosmos = testKosmos()
 
     private val mockActivityStarter = kosmos.activityStarter
-    private val fakeBrightnessPolicyEnforcementInteractor = kosmos.fakeBrightnessPolicyRepository
 
     private val underTest =
         with(kosmos) {
@@ -70,7 +69,18 @@
 
                 fakeBrightnessPolicyRepository.setCurrentUserRestricted()
 
-                assertThat(restriction).isInstanceOf(PolicyRestriction.Restricted::class.java)
+                assertThat(restriction)
+                    .isEqualTo(
+                        PolicyRestriction.Restricted(
+                            EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(
+                                BrightnessPolicyRepository.RESTRICTION
+                            )
+                        )
+                    )
+
+                fakeBrightnessPolicyRepository.setBaseUserRestriction()
+
+                assertThat(restriction).isEqualTo(PolicyRestriction.Restricted(EnforcedAdmin()))
             }
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index b4b812d..0ab0959 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -265,7 +265,7 @@
         with(kosmos) {
             testScope.runTest {
                 // Device is dreaming and on communal.
-                fakeKeyguardRepository.setDreaming(true)
+                updateDreaming(true)
                 communalInteractor.changeScene(CommunalScenes.Communal)
 
                 val scene by collectLastValue(communalInteractor.desiredScene)
@@ -282,7 +282,7 @@
         with(kosmos) {
             testScope.runTest {
                 // Device is not dreaming and on communal.
-                fakeKeyguardRepository.setDreaming(false)
+                updateDreaming(false)
                 communalInteractor.changeScene(CommunalScenes.Communal)
 
                 // Scene stays as Communal
@@ -297,7 +297,7 @@
         with(kosmos) {
             testScope.runTest {
                 // Device is dreaming and on communal.
-                fakeKeyguardRepository.setDreaming(true)
+                updateDreaming(true)
                 communalInteractor.changeScene(CommunalScenes.Communal)
 
                 val scene by collectLastValue(communalInteractor.desiredScene)
@@ -309,7 +309,7 @@
 
                 // Dream stops, timeout is cancelled and device stays on hub, because the regular
                 // screen timeout will take effect at this point.
-                fakeKeyguardRepository.setDreaming(false)
+                updateDreaming(false)
                 advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
             }
@@ -320,7 +320,7 @@
         with(kosmos) {
             testScope.runTest {
                 // Device is on communal, but not dreaming.
-                fakeKeyguardRepository.setDreaming(false)
+                updateDreaming(false)
                 communalInteractor.changeScene(CommunalScenes.Communal)
 
                 val scene by collectLastValue(communalInteractor.desiredScene)
@@ -328,7 +328,7 @@
 
                 // Wait a bit, but not long enough to timeout, then start dreaming.
                 advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                fakeKeyguardRepository.setDreaming(true)
+                updateDreaming(true)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 // Device times out after one screen timeout interval, dream doesn't reset timeout.
@@ -338,11 +338,31 @@
         }
 
     @Test
+    fun hubTimeout_dreamAfterInitialTimeout_goesToBlank() =
+        with(kosmos) {
+            testScope.runTest {
+                // Device is on communal.
+                communalInteractor.changeScene(CommunalScenes.Communal)
+
+                // Device stays on the hub after the timeout since we're not dreaming.
+                advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
+                val scene by collectLastValue(communalInteractor.desiredScene)
+                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+
+                // Start dreaming.
+                updateDreaming(true)
+
+                // Hub times out immediately.
+                assertThat(scene).isEqualTo(CommunalScenes.Blank)
+            }
+        }
+
+    @Test
     fun hubTimeout_userActivityTriggered_resetsTimeout() =
         with(kosmos) {
             testScope.runTest {
                 // Device is dreaming and on communal.
-                fakeKeyguardRepository.setDreaming(true)
+                updateDreaming(true)
                 communalInteractor.changeScene(CommunalScenes.Communal)
 
                 val scene by collectLastValue(communalInteractor.desiredScene)
@@ -371,7 +391,7 @@
                 fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2)
 
                 // Device is dreaming and on communal.
-                fakeKeyguardRepository.setDreaming(true)
+                updateDreaming(true)
                 communalInteractor.changeScene(CommunalScenes.Communal)
 
                 val scene by collectLastValue(communalInteractor.desiredScene)
@@ -395,6 +415,12 @@
             runCurrent()
         }
 
+    private fun TestScope.updateDreaming(dreaming: Boolean) =
+        with(kosmos) {
+            fakeKeyguardRepository.setDreaming(dreaming)
+            runCurrent()
+        }
+
     private suspend fun TestScope.enableCommunal() =
         with(kosmos) {
             setCommunalAvailable(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
index 1cdc2b6..407bf4c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -114,7 +114,7 @@
 
             // Change to media unavailable and notify the listener.
             whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(false)
-            mediaDataListenerCaptor.value.onMediaDataRemoved("key")
+            mediaDataListenerCaptor.value.onMediaDataRemoved("key", false)
             runCurrent()
 
             // Media active now returns false.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index ce7b60e..325a324 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -29,6 +29,7 @@
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.settingslib.flags.Flags.FLAG_ALLOW_ALL_WIDGETS_ON_LOCKSCREEN_BY_DEFAULT
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.broadcastDispatcher
@@ -202,6 +203,18 @@
                 .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
         }
 
+    @EnableFlags(FLAG_COMMUNAL_HUB, FLAG_ALLOW_ALL_WIDGETS_ON_LOCKSCREEN_BY_DEFAULT)
+    @Test
+    fun hubShowsAllWidgetsByDefaultWhenFlagEnabled() =
+        testScope.runTest {
+            val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER))
+            assertThat(setting?.categories)
+                .isEqualTo(
+                    AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD +
+                        AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN
+                )
+        }
+
     private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
         whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
             .thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 766798c..83227e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -204,14 +204,14 @@
         }
 
     @Test
-    fun isCommunalAvailable_whenDreaming_true() =
+    fun isCommunalAvailable_whenKeyguardShowing_true() =
         testScope.runTest {
             val isAvailable by collectLastValue(underTest.isCommunalAvailable)
             assertThat(isAvailable).isFalse()
 
             keyguardRepository.setIsEncryptedOrLockdown(false)
             userRepository.setSelectedUserInfo(mainUser)
-            keyguardRepository.setDreaming(true)
+            keyguardRepository.setKeyguardShowing(true)
 
             assertThat(isAvailable).isTrue()
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 1f8cb8a..be44339 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -44,7 +44,6 @@
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS
 import com.android.systemui.communal.ui.viewmodel.PopupType
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -52,6 +51,7 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -142,10 +142,10 @@
                 testScope,
                 context.resources,
                 kosmos.keyguardTransitionInteractor,
+                kosmos.keyguardInteractor,
                 kosmos.communalInteractor,
                 kosmos.communalTutorialInteractor,
                 kosmos.shadeInteractor,
-                kosmos.deviceEntryInteractor,
                 mediaHost,
                 logcatLogBuffer("CommunalViewModelTest"),
             )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index e3dd9ae..f5c86e0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -27,11 +27,15 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.DreamManager;
 import android.content.res.Resources;
 import android.graphics.Region;
 import android.os.Handler;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.AttachedSurfaceControl;
+import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
@@ -41,12 +45,15 @@
 
 import com.android.dream.lowlight.LowLightTransitionCoordinator;
 import com.android.keyguard.BouncerPanelExpansionCalculator;
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
 import com.android.systemui.complication.ComplicationHostViewController;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.BlurUtils;
 
 import kotlinx.coroutines.CoroutineDispatcher;
@@ -91,6 +98,9 @@
     ViewGroup mDreamOverlayContentView;
 
     @Mock
+    View mHubGestureIndicatorView;
+
+    @Mock
     Handler mHandler;
 
     @Mock
@@ -115,6 +125,12 @@
     DreamOverlayStateController mStateController;
     @Mock
     KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    @Mock
+    ShadeInteractor mShadeInteractor;
+    @Mock
+    CommunalInteractor mCommunalInteractor;
+    @Mock
+    private DreamManager mDreamManager;
 
     DreamOverlayContainerViewController mController;
 
@@ -133,6 +149,7 @@
                 mDreamOverlayContainerView,
                 mComplicationHostViewController,
                 mDreamOverlayContentView,
+                mHubGestureIndicatorView,
                 mDreamOverlayStatusBarViewController,
                 mLowLightTransitionCoordinator,
                 mBlurUtils,
@@ -146,7 +163,22 @@
                 mAnimationsController,
                 mStateController,
                 mBouncerlessScrimController,
-                mKeyguardTransitionInteractor);
+                mKeyguardTransitionInteractor,
+                mShadeInteractor,
+                mCommunalInteractor,
+                mDreamManager);
+    }
+
+    @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+    @Test
+    public void testHubGestureIndicatorGoneWhenFlagOff() {
+        verify(mHubGestureIndicatorView, never()).setVisibility(View.VISIBLE);
+    }
+
+    @EnableFlags({Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_GLANCEABLE_HUB_GESTURE_HANDLE})
+    @Test
+    public void testHubGestureIndicatorVisibleWhenFlagOn() {
+        verify(mHubGestureIndicatorView).setVisibility(View.VISIBLE);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index e3c6dee..29fbee0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -108,7 +108,7 @@
         mTouchHandler.onSessionStart(mTouchSession);
         verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
         inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
-        verify(mCentralSurfaces).handleExternalShadeWindowTouch(motionEvent);
+        verify(mCentralSurfaces).handleDreamTouch(motionEvent);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/FingerprintPropertyRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/FingerprintPropertyRepositoryTest.kt
new file mode 100644
index 0000000..1e7ed63
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/FingerprintPropertyRepositoryTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@ExperimentalCoroutinesApi
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class FingerprintPropertyRepositoryTest : SysuiTestCase() {
+    @JvmField @Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val testScope = TestScope()
+    private lateinit var underTest: FingerprintPropertyRepositoryImpl
+    @Mock private lateinit var fingerprintManager: FingerprintManager
+    @Captor
+    private lateinit var fingerprintCallbackCaptor:
+        ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback>
+
+    @Before
+    fun setup() {
+        underTest =
+            FingerprintPropertyRepositoryImpl(
+                testScope.backgroundScope,
+                Dispatchers.Main.immediate,
+                fingerprintManager,
+            )
+    }
+
+    @Test
+    fun propertiesInitialized_onStartFalse() =
+        testScope.runTest {
+            val propertiesInitialized by collectLastValue(underTest.propertiesInitialized)
+            assertThat(propertiesInitialized).isFalse()
+        }
+
+    @Test
+    fun propertiesInitialized_onStartTrue() =
+        testScope.runTest {
+            //            // collect sensorType to update fingerprintCallback before
+            // propertiesInitialized
+            //            // is listened for
+            val sensorType by collectLastValue(underTest.sensorType)
+            runCurrent()
+            captureFingerprintCallback()
+
+            fingerprintCallbackCaptor.value.onAllAuthenticatorsRegistered(emptyList())
+            val propertiesInitialized by collectLastValue(underTest.propertiesInitialized)
+            assertThat(propertiesInitialized).isTrue()
+        }
+
+    @Test
+    fun propertiesInitialized_updatedToTrue() =
+        testScope.runTest {
+            val propertiesInitialized by collectLastValue(underTest.propertiesInitialized)
+            assertThat(propertiesInitialized).isFalse()
+
+            captureFingerprintCallback()
+            fingerprintCallbackCaptor.value.onAllAuthenticatorsRegistered(emptyList())
+            assertThat(propertiesInitialized).isTrue()
+        }
+
+    private fun captureFingerprintCallback() {
+        verify(fingerprintManager)
+            .addAuthenticatorsRegisteredCallback(fingerprintCallbackCaptor.capture())
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index cb2d4e0..addbdb6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -60,17 +60,19 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val repository = kosmos.fakeKeyguardRepository
-    private val sceneInteractor = kosmos.sceneInteractor
-    private val fromGoneTransitionInteractor = kosmos.fromGoneTransitionInteractor
-    private val commandQueue = kosmos.fakeCommandQueue
-    private val configRepository = kosmos.fakeConfigurationRepository
-    private val bouncerRepository = kosmos.keyguardBouncerRepository
-    private val shadeRepository = kosmos.shadeRepository
-    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val repository by lazy { kosmos.fakeKeyguardRepository }
+    private val sceneInteractor by lazy { kosmos.sceneInteractor }
+    private val fromGoneTransitionInteractor by lazy { kosmos.fromGoneTransitionInteractor }
+    private val commandQueue by lazy { kosmos.fakeCommandQueue }
+    private val configRepository by lazy { kosmos.fakeConfigurationRepository }
+    private val bouncerRepository by lazy { kosmos.keyguardBouncerRepository }
+    private val shadeRepository by lazy { kosmos.shadeRepository }
+    private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+
     private val transitionState: MutableStateFlow<ObservableTransitionState> =
         MutableStateFlow(ObservableTransitionState.Idle(Scenes.Gone))
-    private val underTest = kosmos.keyguardInteractor
+
+    private val underTest by lazy { kosmos.keyguardInteractor }
 
     @Before
     fun setUp() {
@@ -275,6 +277,28 @@
         }
 
     @Test
+    fun keyguardTranslationY_whenNotGoneAndShadeIsReesetEmitsZero() =
+        testScope.runTest {
+            val keyguardTranslationY by collectLastValue(underTest.keyguardTranslationY)
+
+            configRepository.setDimensionPixelSize(
+                R.dimen.keyguard_translate_distance_on_swipe_up,
+                100
+            )
+            configRepository.onAnyConfigurationChange()
+
+            shadeRepository.setLegacyShadeExpansion(1f)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.AOD,
+                to = KeyguardState.LOCKSCREEN,
+                testScope,
+            )
+
+            assertThat(keyguardTranslationY).isEqualTo(0f)
+        }
+
+    @Test
     fun keyguardTranslationY_whenTransitioningToGoneAndShadeIsExpandingEmitsNonZero() =
         testScope.runTest {
             val keyguardTranslationY by collectLastValue(underTest.keyguardTranslationY)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index bf0939c..99cccb2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -19,9 +19,13 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
@@ -29,36 +33,74 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
 import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
 import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
 import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
 import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertEquals
 import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertThrows
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @kotlinx.coroutines.ExperimentalCoroutinesApi
-@android.platform.test.annotations.EnabledOnRavenwood
 class KeyguardTransitionInteractorTest : SysuiTestCase() {
     val kosmos = testKosmos()
     val underTest = kosmos.keyguardTransitionInteractor
     val repository = kosmos.fakeKeyguardTransitionRepository
     val testScope = kosmos.testScope
 
+    private val sceneTransitions =
+        MutableStateFlow<ObservableTransitionState>(
+            ObservableTransitionState.Idle(Scenes.Lockscreen)
+        )
+
+    private val lsToGone =
+        ObservableTransitionState.Transition(
+            Scenes.Lockscreen,
+            Scenes.Gone,
+            flowOf(Scenes.Lockscreen),
+            flowOf(0f),
+            false,
+            flowOf(false)
+        )
+
+    private val goneToLs =
+        ObservableTransitionState.Transition(
+            Scenes.Gone,
+            Scenes.Lockscreen,
+            flowOf(Scenes.Lockscreen),
+            flowOf(0f),
+            false,
+            flowOf(false)
+        )
+
+    @Before
+    fun setUp() {
+        kosmos.sceneContainerRepository.setTransitionState(sceneTransitions)
+    }
+
     @Test
     fun transitionCollectorsReceivesOnlyAppropriateEvents() =
         testScope.runTest {
-            val lockscreenToAodSteps by collectValues(underTest.transition(LOCKSCREEN, AOD))
-            val aodToLockscreenSteps by collectValues(underTest.transition(AOD, LOCKSCREEN))
+            val lockscreenToAodSteps by
+                collectValues(underTest.transition(Edge.create(LOCKSCREEN, AOD)))
+            val aodToLockscreenSteps by
+                collectValues(underTest.transition(Edge.create(AOD, LOCKSCREEN)))
 
             val steps = mutableListOf<TransitionStep>()
             steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
@@ -482,6 +524,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun isInTransitionToState() =
         testScope.runTest {
             val results by collectValues(underTest.isInTransitionToState(GONE))
@@ -586,7 +629,7 @@
                 )
 
             sendSteps(
-                TransitionStep(DOZING, GONE, 0f, STARTED),
+                TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
             )
 
             assertThat(results)
@@ -598,7 +641,7 @@
                 )
 
             sendSteps(
-                TransitionStep(DOZING, GONE, 0f, RUNNING),
+                TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING),
             )
 
             assertThat(results)
@@ -610,7 +653,7 @@
                 )
 
             sendSteps(
-                TransitionStep(DOZING, GONE, 0f, FINISHED),
+                TransitionStep(DOZING, LOCKSCREEN, 0f, FINISHED),
             )
 
             assertThat(results)
@@ -623,9 +666,9 @@
                 )
 
             sendSteps(
-                TransitionStep(GONE, DOZING, 0f, STARTED),
-                TransitionStep(GONE, DOZING, 0f, RUNNING),
-                TransitionStep(GONE, DOZING, 1f, FINISHED),
+                TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED),
+                TransitionStep(LOCKSCREEN, DOZING, 0f, RUNNING),
+                TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED),
             )
 
             assertThat(results)
@@ -638,8 +681,8 @@
                 )
 
             sendSteps(
-                TransitionStep(DOZING, GONE, 0f, STARTED),
-                TransitionStep(DOZING, GONE, 0f, RUNNING),
+                TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
+                TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING),
             )
 
             assertThat(results)
@@ -1404,6 +1447,143 @@
             )
         }
 
+    @Test
+    @DisableSceneContainer
+    fun transition_no_conversion_with_flag_off() =
+        testScope.runTest {
+            val currentStates by
+                collectValues(underTest.transition(Edge.create(PRIMARY_BOUNCER, GONE)))
+
+            val sendStep1 = TransitionStep(PRIMARY_BOUNCER, GONE, 0f, STARTED)
+            sendSteps(sendStep1)
+
+            assertEquals(listOf(sendStep1), currentStates)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun transition_conversion_with_flag_on() =
+        testScope.runTest {
+            val currentStates by
+                collectValues(underTest.transition(Edge.create(PRIMARY_BOUNCER, GONE)))
+
+            val sendStep1 = TransitionStep(PRIMARY_BOUNCER, GONE, 0f, STARTED)
+            sendSteps(sendStep1)
+
+            assertEquals(listOf<TransitionStep>(), currentStates)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun transition_conversion_emits_values_with_sceneContainer_in_correct_state() =
+        testScope.runTest {
+            val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, GONE)))
+            val currentStatesConverted by
+                collectValues(underTest.transition(Edge.create(LOCKSCREEN, UNDEFINED)))
+
+            sceneTransitions.value = lsToGone
+            val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+            val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+            val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+            sendSteps(sendStep1, sendStep2, sendStep3)
+
+            assertEquals(listOf(sendStep1, sendStep2), currentStates)
+            assertEquals(listOf(sendStep1, sendStep2), currentStatesConverted)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun transition_conversion_emits_nothing_with_sceneContainer_in_wrong_state() =
+        testScope.runTest {
+            val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, GONE)))
+
+            sceneTransitions.value = goneToLs
+            val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+            val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+            val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+            sendSteps(sendStep1, sendStep2, sendStep3)
+
+            assertEquals(listOf<TransitionStep>(), currentStates)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun transition_conversion_emits_values_when_edge_within_lockscreen_scene() =
+        testScope.runTest {
+            val currentStates by
+                collectValues(underTest.transition(Edge.create(LOCKSCREEN, DOZING)))
+
+            sceneTransitions.value = goneToLs
+            val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
+            val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
+            val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+            sendSteps(sendStep1, sendStep2, sendStep3)
+
+            assertEquals(listOf(sendStep1, sendStep2), currentStates)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun transition_conversion_emits_values_with_null_edge_within_lockscreen_scene() =
+        testScope.runTest {
+            val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, null)))
+            val currentStatesReversed by
+                collectValues(underTest.transition(Edge.create(null, LOCKSCREEN)))
+
+            sceneTransitions.value = goneToLs
+            val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
+            val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
+            val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+            val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
+            sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+
+            assertEquals(listOf(sendStep1, sendStep2, sendStep3), currentStates)
+            assertEquals(listOf(sendStep4), currentStatesReversed)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun transition_conversion_emits_values_with_null_edge_out_of_lockscreen_scene() =
+        testScope.runTest {
+            val currentStates by collectValues(underTest.transition(Edge.create(null, UNDEFINED)))
+            val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE)))
+
+            sceneTransitions.value = lsToGone
+            val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+            val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+            val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
+            val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
+            sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+
+            assertEquals(listOf(sendStep1, sendStep2), currentStates)
+            assertEquals(listOf(sendStep1, sendStep2), currentStatesMapped)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun transition_conversion_does_not_emit_with_null_edge_with_wrong_stl_state() =
+        testScope.runTest {
+            val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE)))
+
+            sceneTransitions.value = goneToLs
+            val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+            val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+            val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
+            val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
+            sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+
+            assertEquals(listOf<TransitionStep>(), currentStatesMapped)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun transition_null_edges_throw() =
+        testScope.runTest {
+            assertThrows(IllegalStateException::class.java) {
+                underTest.transition(Edge.create(null, null))
+            }
+        }
+
     private suspend fun sendSteps(vararg steps: TransitionStep) {
         steps.forEach {
             repository.sendTransitionStep(it)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
index 0ac7ff5..a0fed6b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
@@ -23,11 +23,13 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.milliseconds
@@ -50,11 +52,14 @@
     @Before
     fun setUp() {
         underTest =
-            animationFlow.setup(
-                duration = 1000.milliseconds,
-                from = GONE,
-                to = DREAMING,
-            )
+            animationFlow
+                .setup(
+                    duration = 1000.milliseconds,
+                    edge = Edge.create(from = Scenes.Gone, to = DREAMING),
+                )
+                .setupWithoutSceneContainer(
+                    edge = Edge.create(from = GONE, to = DREAMING),
+                )
     }
 
     @Test(expected = IllegalArgumentException::class)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index e270d9e..519bb6e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -62,8 +62,8 @@
     private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
     private val keyguardClockRepository = kosmos.fakeKeyguardClockRepository
     private lateinit var underTest: AodBurnInViewModel
-
-    private var burnInParameters = BurnInParameters()
+    // assign a smaller value to minViewY to avoid overflow
+    private var burnInParameters = BurnInParameters(minViewY = Int.MAX_VALUE / 2)
     private val burnInFlow = MutableStateFlow(BurnInModel())
 
     @Before
@@ -296,52 +296,111 @@
                     scale = 0.5f,
                 )
 
-            assertThat(movement?.translationX).isEqualTo(0)
-            assertThat(movement?.translationY).isEqualTo(0)
+            assertThat(movement?.translationX).isEqualTo(20)
+            assertThat(movement?.translationY).isEqualTo(30)
             assertThat(movement?.scale).isEqualTo(0.5f)
             assertThat(movement?.scaleClockOnly).isEqualTo(false)
         }
 
     @Test
-    fun translationAndScale_composeFlagOn_weatherLargeClock() =
-        testBurnInViewModelWhenComposeFlagOn(
+    fun translationAndScale_composeFlagOff_weatherLargeClock() =
+        testBurnInViewModelForClocks(
             isSmallClock = false,
             isWeatherClock = true,
-            expectedScaleOnly = false
+            expectedScaleOnly = false,
+            enableMigrateClocksToBlueprintFlag = true,
+            enableComposeLockscreenFlag = false
+        )
+
+    @Test
+    fun translationAndScale_composeFlagOff_weatherSmallClock() =
+        testBurnInViewModelForClocks(
+            isSmallClock = true,
+            isWeatherClock = true,
+            expectedScaleOnly = false,
+            enableMigrateClocksToBlueprintFlag = true,
+            enableComposeLockscreenFlag = false
+        )
+
+    @Test
+    fun translationAndScale_composeFlagOff_nonWeatherLargeClock() =
+        testBurnInViewModelForClocks(
+            isSmallClock = false,
+            isWeatherClock = false,
+            expectedScaleOnly = true,
+            enableMigrateClocksToBlueprintFlag = true,
+            enableComposeLockscreenFlag = false
+        )
+
+    @Test
+    fun translationAndScale_composeFlagOff_nonWeatherSmallClock() =
+        testBurnInViewModelForClocks(
+            isSmallClock = true,
+            isWeatherClock = false,
+            expectedScaleOnly = false,
+            enableMigrateClocksToBlueprintFlag = true,
+            enableComposeLockscreenFlag = false
+        )
+
+    @Test
+    fun translationAndScale_composeFlagOn_weatherLargeClock() =
+        testBurnInViewModelForClocks(
+            isSmallClock = false,
+            isWeatherClock = true,
+            expectedScaleOnly = false,
+            enableMigrateClocksToBlueprintFlag = true,
+            enableComposeLockscreenFlag = true
         )
 
     @Test
     fun translationAndScale_composeFlagOn_weatherSmallClock() =
-        testBurnInViewModelWhenComposeFlagOn(
+        testBurnInViewModelForClocks(
             isSmallClock = true,
             isWeatherClock = true,
-            expectedScaleOnly = true
+            expectedScaleOnly = false,
+            enableMigrateClocksToBlueprintFlag = true,
+            enableComposeLockscreenFlag = true
         )
 
     @Test
     fun translationAndScale_composeFlagOn_nonWeatherLargeClock() =
-        testBurnInViewModelWhenComposeFlagOn(
+        testBurnInViewModelForClocks(
             isSmallClock = false,
             isWeatherClock = false,
-            expectedScaleOnly = true
+            expectedScaleOnly = true,
+            enableMigrateClocksToBlueprintFlag = true,
+            enableComposeLockscreenFlag = true
         )
 
     @Test
     fun translationAndScale_composeFlagOn_nonWeatherSmallClock() =
-        testBurnInViewModelWhenComposeFlagOn(
+        testBurnInViewModelForClocks(
             isSmallClock = true,
             isWeatherClock = false,
-            expectedScaleOnly = true
+            expectedScaleOnly = false,
+            enableMigrateClocksToBlueprintFlag = true,
+            enableComposeLockscreenFlag = true
         )
 
-    private fun testBurnInViewModelWhenComposeFlagOn(
+    private fun testBurnInViewModelForClocks(
         isSmallClock: Boolean,
         isWeatherClock: Boolean,
-        expectedScaleOnly: Boolean
+        expectedScaleOnly: Boolean,
+        enableMigrateClocksToBlueprintFlag: Boolean,
+        enableComposeLockscreenFlag: Boolean
     ) =
         testScope.runTest {
-            mSetFlagsRule.enableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-            mSetFlagsRule.enableFlags(AConfigFlags.FLAG_COMPOSE_LOCKSCREEN)
+            if (enableMigrateClocksToBlueprintFlag) {
+                mSetFlagsRule.enableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+            } else {
+                mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+            }
+
+            if (enableComposeLockscreenFlag) {
+                mSetFlagsRule.enableFlags(AConfigFlags.FLAG_COMPOSE_LOCKSCREEN)
+            } else {
+                mSetFlagsRule.disableFlags(AConfigFlags.FLAG_COMPOSE_LOCKSCREEN)
+            }
             if (isSmallClock) {
                 keyguardClockRepository.setClockSize(ClockSize.SMALL)
                 // we need the following step to update stateFlow value
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
index d632936..7a9bd92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -87,6 +87,7 @@
     }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun scrimAlpha_runDimissFromKeyguard_shadeExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
@@ -137,6 +138,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun scrimBehindAlpha_leaveShadeOpen() =
         testScope.runTest {
             val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
@@ -161,6 +163,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun showAllNotifications_isTrue_whenLeaveShadeOpen() =
         testScope.runTest {
             val showAllNotifications by
@@ -177,6 +180,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun showAllNotifications_isFalse_whenLeaveShadeIsNotOpen() =
         testScope.runTest {
             val showAllNotifications by
@@ -193,6 +197,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(330311871)
     fun scrimBehindAlpha_doNotLeaveShadeOpen() =
         testScope.runTest {
             val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 838b2a7..20ffa33 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -30,6 +30,8 @@
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.parameterizeSceneContainerFlag
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -37,7 +39,9 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
 import com.android.systemui.statusbar.phone.dozeParameters
@@ -49,6 +53,8 @@
 import com.android.systemui.util.ui.value
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -75,6 +81,11 @@
 
     private val viewState = ViewStateAccessor()
 
+    private val transitionState =
+        MutableStateFlow<ObservableTransitionState>(
+            ObservableTransitionState.Idle(Scenes.Lockscreen)
+        )
+
     companion object {
         @JvmStatic
         @Parameters(name = "{0}")
@@ -96,6 +107,7 @@
                 AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
             )
         }
+        kosmos.sceneContainerRepository.setTransitionState(transitionState)
     }
 
     @Test
@@ -309,6 +321,32 @@
         }
 
     @Test
+    @EnableSceneContainer
+    fun alpha_transitionToHub_isZero_scene_container() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.alpha(viewState))
+
+            transitionState.value =
+                ObservableTransitionState.Transition(
+                    fromScene = Scenes.Lockscreen,
+                    toScene = Scenes.Communal,
+                    emptyFlow(),
+                    emptyFlow(),
+                    false,
+                    emptyFlow()
+                )
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                testScope,
+            )
+
+            assertThat(alpha).isEqualTo(0f)
+        }
+
+    @Test
+    @DisableSceneContainer
     fun alpha_transitionToHub_isZero() =
         testScope.runTest {
             val alpha by collectLastValue(underTest.alpha(viewState))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index bc9d257..f46ca00 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -24,6 +24,7 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -39,6 +40,7 @@
 import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
@@ -117,6 +119,17 @@
             }
         }
 
+        private fun expectedDownTransitionKey(
+            isSingleShade: Boolean,
+            isShadeTouchable: Boolean,
+        ): TransitionKey? {
+            return when {
+                !isShadeTouchable -> null
+                !isSingleShade -> TransitionKeys.ToSplitShade
+                else -> null
+            }
+        }
+
         private fun expectedUpDestination(
             canSwipeToEnter: Boolean,
             isShadeTouchable: Boolean,
@@ -184,18 +197,16 @@
             )
 
             val destinationScenes by collectLastValue(underTest.destinationScenes)
-
-            assertThat(
-                    destinationScenes
-                        ?.get(
-                            Swipe(
-                                SwipeDirection.Down,
-                                fromSource = Edge.Top.takeIf { downFromEdge },
-                                pointerCount = if (downWithTwoPointers) 2 else 1,
-                            )
-                        )
-                        ?.toScene
+            val downDestination =
+                destinationScenes?.get(
+                    Swipe(
+                        SwipeDirection.Down,
+                        fromSource = Edge.Top.takeIf { downFromEdge },
+                        pointerCount = if (downWithTwoPointers) 2 else 1,
+                    )
                 )
+
+            assertThat(downDestination?.toScene)
                 .isEqualTo(
                     expectedDownDestination(
                         downFromEdge = downFromEdge,
@@ -204,6 +215,14 @@
                     )
                 )
 
+            assertThat(downDestination?.transitionKey)
+                .isEqualTo(
+                    expectedDownTransitionKey(
+                        isSingleShade = isSingleShade,
+                        isShadeTouchable = isShadeTouchable,
+                    )
+                )
+
             assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
                 .isEqualTo(
                     expectedUpDestination(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index 58c6817..1c1fcc4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -18,8 +18,10 @@
 
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -30,11 +32,16 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
 import com.google.common.truth.Truth
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -58,6 +65,11 @@
     private val keyguardRepository = kosmos.fakeKeyguardRepository
     private lateinit var underTest: LockscreenToPrimaryBouncerTransitionViewModel
 
+    private val transitionState =
+        MutableStateFlow<ObservableTransitionState>(
+            ObservableTransitionState.Idle(Scenes.Lockscreen)
+        )
+
     companion object {
         @JvmStatic
         @Parameters(name = "{0}")
@@ -76,6 +88,7 @@
     }
 
     @Test
+    @BrokenWithSceneContainer(330311871)
     fun deviceEntryParentViewAlpha_shadeExpanded() =
         testScope.runTest {
             val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
@@ -107,6 +120,17 @@
             shadeExpanded(false)
             runCurrent()
 
+            kosmos.sceneContainerRepository.setTransitionState(transitionState)
+            transitionState.value =
+                ObservableTransitionState.Transition(
+                    fromScene = Scenes.Lockscreen,
+                    toScene = Scenes.Bouncer,
+                    emptyFlow(),
+                    emptyFlow(),
+                    false,
+                    emptyFlow()
+                )
+            runCurrent()
             // fade out
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             runCurrent()
@@ -132,7 +156,9 @@
     ): TransitionStep {
         return TransitionStep(
             from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.PRIMARY_BOUNCER,
+            to =
+                if (SceneContainerFlag.isEnabled) KeyguardState.UNDEFINED
+                else KeyguardState.PRIMARY_BOUNCER,
             value = value,
             transitionState = state,
             ownerName = "LockscreenToPrimaryBouncerTransitionViewModelTest"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
index 1e5f314..7a37a9e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
@@ -150,7 +150,7 @@
     @Test
     fun addMediaControlPlayingThenRemote() =
         testScope.runTest {
-            val sortedMedia by collectLastValue(underTest.sortedMedia)
+            val currentMedia by collectLastValue(underTest.currentMedia)
             val playingInstanceId = InstanceId.fakeInstanceId(123)
             val remoteInstanceId = InstanceId.fakeInstanceId(321)
             val playingData = createMediaData("app1", true, LOCAL, false, playingInstanceId)
@@ -161,8 +161,8 @@
             underTest.addSelectedUserMediaEntry(remoteData)
             underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(remoteInstanceId))
 
-            assertThat(sortedMedia?.size).isEqualTo(2)
-            assertThat(sortedMedia?.values)
+            assertThat(currentMedia?.size).isEqualTo(2)
+            assertThat(currentMedia)
                 .containsExactly(
                     MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId)),
                     MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(remoteInstanceId))
@@ -173,7 +173,7 @@
     @Test
     fun switchMediaControlsPlaying() =
         testScope.runTest {
-            val sortedMedia by collectLastValue(underTest.sortedMedia)
+            val currentMedia by collectLastValue(underTest.currentMedia)
             val playingInstanceId1 = InstanceId.fakeInstanceId(123)
             val playingInstanceId2 = InstanceId.fakeInstanceId(321)
             var playingData1 = createMediaData("app1", true, LOCAL, false, playingInstanceId1)
@@ -184,8 +184,8 @@
             underTest.addSelectedUserMediaEntry(playingData2)
             underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2))
 
-            assertThat(sortedMedia?.size).isEqualTo(2)
-            assertThat(sortedMedia?.values)
+            assertThat(currentMedia?.size).isEqualTo(2)
+            assertThat(currentMedia)
                 .containsExactly(
                     MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1)),
                     MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId2))
@@ -198,12 +198,28 @@
             underTest.addSelectedUserMediaEntry(playingData1)
             underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId1))
             underTest.addSelectedUserMediaEntry(playingData2)
-            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2))
+            underTest.addMediaDataLoadingState(
+                MediaDataLoadingModel.Loaded(playingInstanceId2, false)
+            )
 
-            assertThat(sortedMedia?.size).isEqualTo(2)
-            assertThat(sortedMedia?.values)
+            assertThat(currentMedia?.size).isEqualTo(2)
+            assertThat(currentMedia)
                 .containsExactly(
-                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId2)),
+                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1)),
+                    MediaCommonModel.MediaControl(
+                        MediaDataLoadingModel.Loaded(playingInstanceId2, false)
+                    )
+                )
+                .inOrder()
+
+            underTest.setOrderedMedia()
+
+            assertThat(currentMedia?.size).isEqualTo(2)
+            assertThat(currentMedia)
+                .containsExactly(
+                    MediaCommonModel.MediaControl(
+                        MediaDataLoadingModel.Loaded(playingInstanceId2, false)
+                    ),
                     MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1))
                 )
                 .inOrder()
@@ -212,7 +228,7 @@
     @Test
     fun fullOrderTest() =
         testScope.runTest {
-            val sortedMedia by collectLastValue(underTest.sortedMedia)
+            val currentMedia by collectLastValue(underTest.currentMedia)
             val instanceId1 = InstanceId.fakeInstanceId(123)
             val instanceId2 = InstanceId.fakeInstanceId(456)
             val instanceId3 = InstanceId.fakeInstanceId(321)
@@ -252,8 +268,8 @@
                 SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
             )
 
-            assertThat(sortedMedia?.size).isEqualTo(6)
-            assertThat(sortedMedia?.values)
+            assertThat(currentMedia?.size).isEqualTo(6)
+            assertThat(currentMedia)
                 .containsExactly(
                     MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(instanceId1)),
                     MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(instanceId2)),
@@ -270,7 +286,7 @@
     @Test
     fun loadMediaFromRec() =
         testScope.runTest {
-            val isMediaFromRec by collectLastValue(underTest.isMediaFromRec)
+            val currentMedia by collectLastValue(underTest.currentMedia)
             val instanceId1 = InstanceId.fakeInstanceId(123)
             val instanceId2 = InstanceId.fakeInstanceId(456)
             val data =
@@ -278,22 +294,59 @@
                     active = true,
                     instanceId = instanceId1,
                     packageName = PACKAGE_NAME,
-                    isPlaying = true
+                    isPlaying = true,
+                    notificationKey = KEY,
                 )
-            val newData = MediaData(active = true, instanceId = instanceId2)
-
-            assertThat(isMediaFromRec).isFalse()
+            val newData =
+                MediaData(
+                    active = true,
+                    instanceId = instanceId2,
+                    isPlaying = true,
+                    notificationKey = KEY_2
+                )
+            val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
+            val mediaRecommendations =
+                SmartspaceMediaData(
+                    targetId = KEY_MEDIA_SMARTSPACE,
+                    isActive = true,
+                    packageName = PACKAGE_NAME,
+                    recommendations = MediaTestHelper.getValidRecommendationList(icon),
+                )
 
             underTest.setMediaFromRecPackageName(PACKAGE_NAME)
             underTest.addSelectedUserMediaEntry(data)
+            underTest.setRecommendation(mediaRecommendations)
+            underTest.setRecommendationsLoadingState(
+                SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
+            )
             underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId1))
 
-            assertThat(isMediaFromRec).isTrue()
+            assertThat(currentMedia)
+                .containsExactly(
+                    MediaCommonModel.MediaControl(
+                        MediaDataLoadingModel.Loaded(instanceId1),
+                        isMediaFromRec = true
+                    ),
+                    MediaCommonModel.MediaRecommendations(
+                        SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
+                    )
+                )
+                .inOrder()
 
             underTest.addSelectedUserMediaEntry(newData)
+            underTest.addSelectedUserMediaEntry(data.copy(isPlaying = false))
             underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId2))
+            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId1))
 
-            assertThat(isMediaFromRec).isFalse()
+            assertThat(currentMedia)
+                .containsExactly(
+                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(instanceId2)),
+                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(instanceId1)),
+                    MediaCommonModel.MediaRecommendations(
+                        SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
+                    )
+                )
+                .inOrder()
         }
 
     private fun createMediaData(
@@ -316,6 +369,7 @@
         private const val LOCAL = MediaData.PLAYBACK_LOCAL
         private const val REMOTE = MediaData.PLAYBACK_CAST_LOCAL
         private const val KEY = "KEY"
+        private const val KEY_2 = "KEY_2"
         private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
         private const val PACKAGE_NAME = "com.android.example"
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
index e44affc7..39dbc7e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
@@ -55,6 +55,14 @@
     private val mediaFilterRepository: MediaFilterRepository = kosmos.mediaFilterRepository
     private val mediaRecommendationsInteractor: MediaRecommendationsInteractor =
         kosmos.mediaRecommendationsInteractor
+    val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
+    private val mediaRecommendation =
+        SmartspaceMediaData(
+            targetId = KEY_MEDIA_SMARTSPACE,
+            isActive = true,
+            packageName = PACKAGE_NAME,
+            recommendations = MediaTestHelper.getValidRecommendationList(icon),
+        )
 
     private val underTest: MediaCarouselInteractor = kosmos.mediaCarouselInteractor
 
@@ -122,26 +130,19 @@
                 collectLastValue(underTest.hasActiveMediaOrRecommendation)
             val hasAnyMediaOrRecommendation by
                 collectLastValue(underTest.hasAnyMediaOrRecommendation)
-            val sortedMedia by collectLastValue(underTest.sortedMedia)
+            val currentMedia by collectLastValue(underTest.currentMedia)
             kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
 
-            val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
-            val userMediaRecommendation =
-                SmartspaceMediaData(
-                    targetId = KEY_MEDIA_SMARTSPACE,
-                    isActive = true,
-                    recommendations = MediaTestHelper.getValidRecommendationList(icon),
-                )
             val userMedia = MediaData(active = false)
             val recsLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
             val mediaLoadingModel = MediaDataLoadingModel.Loaded(userMedia.instanceId)
 
-            mediaFilterRepository.setRecommendation(userMediaRecommendation)
+            mediaFilterRepository.setRecommendation(mediaRecommendation)
             mediaFilterRepository.setRecommendationsLoadingState(recsLoadingModel)
 
             assertThat(hasActiveMediaOrRecommendation).isTrue()
             assertThat(hasAnyMediaOrRecommendation).isTrue()
-            assertThat(sortedMedia)
+            assertThat(currentMedia)
                 .containsExactly(MediaCommonModel.MediaRecommendations(recsLoadingModel))
 
             mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
@@ -149,7 +150,7 @@
 
             assertThat(hasActiveMediaOrRecommendation).isTrue()
             assertThat(hasAnyMediaOrRecommendation).isTrue()
-            assertThat(sortedMedia)
+            assertThat(currentMedia)
                 .containsExactly(
                     MediaCommonModel.MediaRecommendations(recsLoadingModel),
                     MediaCommonModel.MediaControl(mediaLoadingModel, true)
@@ -166,14 +167,6 @@
                 collectLastValue(underTest.hasAnyMediaOrRecommendation)
             kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
 
-            val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
-            val mediaRecommendation =
-                SmartspaceMediaData(
-                    targetId = KEY_MEDIA_SMARTSPACE,
-                    isActive = true,
-                    recommendations = MediaTestHelper.getValidRecommendationList(icon),
-                )
-
             mediaFilterRepository.setRecommendation(mediaRecommendation)
 
             assertThat(hasActiveMediaOrRecommendation).isTrue()
@@ -194,14 +187,6 @@
                 collectLastValue(underTest.hasAnyMediaOrRecommendation)
             kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
 
-            val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
-            val mediaRecommendation =
-                SmartspaceMediaData(
-                    targetId = KEY_MEDIA_SMARTSPACE,
-                    isActive = true,
-                    recommendations = MediaTestHelper.getValidRecommendationList(icon),
-                )
-
             mediaFilterRepository.setRecommendation(mediaRecommendation)
 
             assertThat(hasActiveMediaOrRecommendation).isTrue()
@@ -234,26 +219,42 @@
     @Test
     fun loadMediaFromRec() =
         testScope.runTest {
-            val isMediaFromRec by collectLastValue(underTest.isMediaFromRec)
+            val currentMedia by collectLastValue(underTest.currentMedia)
             val instanceId = InstanceId.fakeInstanceId(123)
-            val data = MediaData(active = true, instanceId = instanceId, packageName = PACKAGE_NAME)
+            val data =
+                MediaData(
+                    active = true,
+                    instanceId = instanceId,
+                    packageName = PACKAGE_NAME,
+                    notificationKey = KEY
+                )
+            val smartspaceLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
+            val mediaLoadingModel = MediaDataLoadingModel.Loaded(instanceId)
 
-            assertThat(isMediaFromRec).isFalse()
-
+            mediaFilterRepository.setRecommendation(mediaRecommendation)
+            mediaFilterRepository.setRecommendationsLoadingState(smartspaceLoadingModel)
             mediaRecommendationsInteractor.switchToMediaControl(PACKAGE_NAME)
             mediaFilterRepository.addSelectedUserMediaEntry(data)
-            mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+            mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
 
-            assertThat(isMediaFromRec).isFalse()
+            assertThat(currentMedia)
+                .containsExactly(MediaCommonModel.MediaRecommendations(smartspaceLoadingModel))
+                .inOrder()
 
             mediaFilterRepository.addSelectedUserMediaEntry(data.copy(isPlaying = true))
             mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
 
-            assertThat(isMediaFromRec).isTrue()
+            assertThat(currentMedia)
+                .containsExactly(
+                    MediaCommonModel.MediaControl(mediaLoadingModel, isMediaFromRec = true),
+                    MediaCommonModel.MediaRecommendations(smartspaceLoadingModel)
+                )
+                .inOrder()
         }
 
     companion object {
         private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
         private const val PACKAGE_NAME = "com.android.example"
+        private const val KEY = "key"
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
index d9224d7..365a7c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
@@ -27,12 +27,16 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.bluetooth.mockBroadcastDialogController
+import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.data.repository.mediaDataRepository
 import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
+import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaControlInteractor
 import com.android.systemui.media.controls.domain.pipeline.interactor.mediaControlInteractor
 import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
+import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor
 import com.android.systemui.media.controls.shared.model.MediaData
 import com.android.systemui.media.controls.util.mediaInstanceId
 import com.android.systemui.media.mediaOutputDialogManager
@@ -41,16 +45,16 @@
 import com.android.systemui.statusbar.notificationLockscreenUserManager
 import com.android.systemui.statusbar.policy.keyguardStateController
 import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -117,7 +121,7 @@
         whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
             .thenReturn(true)
 
-        val clickIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+        val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
         val expandable = mock<Expandable>()
 
         underTest.startClickIntent(expandable, clickIntent)
@@ -129,7 +133,7 @@
     fun startClickIntent_hideOverLockscreen() {
         whenever(keyguardStateController.isShowing).thenReturn(false)
 
-        val clickIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+        val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
         val expandable = mock<Expandable>()
         val activityController = mock<ActivityTransitionAnimator.Controller>()
         whenever(expandable.activityTransitionController(any())).thenReturn(activityController)
@@ -146,7 +150,7 @@
         whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
             .thenReturn(true)
 
-        val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+        val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
 
         underTest.startDeviceIntent(deviceIntent)
 
@@ -159,7 +163,7 @@
         whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
             .thenReturn(true)
 
-        val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(false) }
+        val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(false) }
 
         underTest.startDeviceIntent(deviceIntent)
 
@@ -170,7 +174,7 @@
     fun startDeviceIntent_hideOverLockscreen() {
         whenever(keyguardStateController.isShowing).thenReturn(false)
 
-        val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+        val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
 
         underTest.startDeviceIntent(deviceIntent)
 
@@ -211,6 +215,21 @@
             )
     }
 
+    @Test
+    fun removeMediaControl() {
+        val listener = mock<MediaDataProcessor.Listener>()
+        kosmos.mediaDataProcessor.addInternalListener(listener)
+
+        var mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+        kosmos.mediaDataRepository.addMediaEntry(KEY, mediaData)
+
+        underTest.removeMediaControl(null, instanceId, 0L)
+        kosmos.fakeExecutor.advanceClockToNext()
+        kosmos.fakeExecutor.runAllReady()
+
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
+    }
+
     companion object {
         private const val USER_ID = 0
         private const val KEY = "key"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
index d1e475f..0551bfb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
@@ -92,13 +92,29 @@
             val instanceId1 = InstanceId.fakeInstanceId(123)
             val instanceId2 = InstanceId.fakeInstanceId(456)
 
-            loadMediaControl(KEY, instanceId1)
-            loadMediaControl(KEY_2, instanceId2)
+            loadMediaControl(KEY, instanceId1, isPlaying = true)
+            loadMediaControl(KEY_2, instanceId2, isPlaying = true)
+            loadMediaControl(KEY, instanceId1, isPlaying = false)
 
-            val firstMediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
-            val secondMediaControl = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
-            assertThat(firstMediaControl.instanceId).isEqualTo(instanceId2)
-            assertThat(secondMediaControl.instanceId).isEqualTo(instanceId1)
+            var mediaControl2 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
+            var mediaControl1 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
+            assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
+            assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)
+
+            loadMediaControl(KEY, instanceId1, isPlaying = true)
+            loadMediaControl(KEY_2, instanceId2, isPlaying = false)
+
+            mediaControl2 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
+            mediaControl1 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
+            assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
+            assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)
+
+            underTest.onReorderingAllowed()
+
+            mediaControl1 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
+            mediaControl2 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
+            assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)
+            assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
         }
 
     @Test
@@ -147,6 +163,7 @@
             val mediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
             assertThat(sortedMedia).hasSize(2)
             assertThat(mediaControl.instanceId).isEqualTo(instanceId)
+            assertThat(mediaControl.isMediaFromRec).isTrue()
         }
 
     private fun loadMediaControl(key: String, instanceId: InstanceId, isPlaying: Boolean = true) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
index 5661bd3..9d8ec95 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
@@ -51,8 +51,8 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val sceneInteractor = kosmos.sceneInteractor
-    private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
+    private val sceneInteractor by lazy { kosmos.sceneInteractor }
+    private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
 
     private val underTest = kosmos.notificationsShadeSceneViewModel
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/CloseShadeRightAfterClickTestB339290820.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/CloseShadeRightAfterClickTestB339290820.kt
new file mode 100644
index 0000000..8d1aa73
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/CloseShadeRightAfterClickTestB339290820.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.external
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.content.ServiceConnection
+import android.content.applicationContext
+import android.content.packageManager
+import android.os.Binder
+import android.os.Handler
+import android.os.RemoteException
+import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
+import android.service.quicksettings.Tile
+import android.testing.TestableContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.packageManagerAdapterFacade
+import com.android.systemui.qs.tiles.impl.custom.customTileSpec
+import com.android.systemui.testKosmos
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CloseShadeRightAfterClickTestB339290820 : SysuiTestCase() {
+
+    private val testableContext: TestableContext
+    private val bindDelayExecutor: FakeExecutor
+    private val kosmos =
+        testKosmos().apply {
+            testableContext = testCase.context
+            bindDelayExecutor = FakeExecutor(fakeSystemClock)
+            testableContext.setMockPackageManager(packageManager)
+            customTileSpec = TileSpec.create(testComponentName)
+            applicationContext = ContextWrapperDelayedBind(testableContext, bindDelayExecutor)
+        }
+
+    @Before
+    fun setUp() {
+        kosmos.apply {
+            whenever(packageManager.getPackageUidAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(Binder.getCallingUid())
+            packageManagerAdapterFacade.setIsActive(true)
+            testableContext.addMockService(testComponentName, iQSTileService.asBinder())
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
+    fun testStopListeningShortlyAfterClick_clickIsSent() {
+        with(kosmos) {
+            val tile = FakeCustomTileInterface(tileServices)
+            // Flush any bind from startup
+            FakeExecutor.exhaustExecutors(fakeExecutor, bindDelayExecutor)
+
+            // Open QS
+            tile.setListening(true)
+            fakeExecutor.runAllReady()
+            tile.click()
+            fakeExecutor.runAllReady()
+
+            // No clicks yet because the latch is preventing the bind
+            assertThat(iQSTileService.clicks).isEmpty()
+
+            // Close QS
+            tile.setListening(false)
+            fakeExecutor.runAllReady()
+            // And finally bind
+            FakeExecutor.exhaustExecutors(fakeExecutor, bindDelayExecutor)
+
+            assertThat(iQSTileService.clicks).containsExactly(tile.token)
+        }
+    }
+}
+
+private val testComponentName = ComponentName("pkg", "srv")
+
+// This is a fake `CustomTile` that implements what we need for the test. Mainly setListening and
+// click
+private class FakeCustomTileInterface(tileServices: TileServices) : CustomTileInterface {
+    override val user: Int
+        get() = 0
+    override val qsTile: Tile = Tile()
+    override val component: ComponentName = testComponentName
+    private var listening = false
+    private val serviceManager = tileServices.getTileWrapper(this)
+    private val serviceInterface = serviceManager.tileService
+
+    val token = Binder()
+
+    override fun getTileSpec(): String {
+        return CustomTile.toSpec(component)
+    }
+
+    override fun refreshState() {}
+
+    override fun updateTileState(tile: Tile, uid: Int) {}
+
+    override fun onDialogShown() {}
+
+    override fun onDialogHidden() {}
+
+    override fun startActivityAndCollapse(pendingIntent: PendingIntent) {}
+
+    override fun startUnlockAndRun() {}
+
+    fun setListening(listening: Boolean) {
+        if (listening == this.listening) return
+        this.listening = listening
+
+        try {
+            if (listening) {
+                if (!serviceManager.isActiveTile) {
+                    serviceManager.setBindRequested(true)
+                    serviceInterface.onStartListening()
+                }
+            } else {
+                serviceInterface.onStopListening()
+                serviceManager.setBindRequested(false)
+            }
+        } catch (e: RemoteException) {
+            // Called through wrapper, won't happen here.
+        }
+    }
+
+    fun click() {
+        try {
+            if (serviceManager.isActiveTile) {
+                serviceManager.setBindRequested(true)
+                serviceInterface.onStartListening()
+            }
+            serviceInterface.onClick(token)
+        } catch (e: RemoteException) {
+            // Called through wrapper, won't happen here.
+        }
+    }
+}
+
+private class ContextWrapperDelayedBind(
+    val context: Context,
+    val executor: FakeExecutor,
+) : ContextWrapper(context) {
+    override fun bindServiceAsUser(
+        service: Intent,
+        conn: ServiceConnection,
+        flags: Int,
+        user: UserHandle
+    ): Boolean {
+        executor.execute { super.bindServiceAsUser(service, conn, flags, user) }
+        return true
+    }
+
+    override fun bindServiceAsUser(
+        service: Intent,
+        conn: ServiceConnection,
+        flags: BindServiceFlags,
+        user: UserHandle
+    ): Boolean {
+        executor.execute { super.bindServiceAsUser(service, conn, flags, user) }
+        return true
+    }
+
+    override fun bindServiceAsUser(
+        service: Intent?,
+        conn: ServiceConnection?,
+        flags: Int,
+        handler: Handler?,
+        user: UserHandle?
+    ): Boolean {
+        executor.execute { super.bindServiceAsUser(service, conn, flags, handler, user) }
+        return true
+    }
+
+    override fun bindServiceAsUser(
+        service: Intent,
+        conn: ServiceConnection,
+        flags: BindServiceFlags,
+        handler: Handler,
+        user: UserHandle
+    ): Boolean {
+        executor.execute { super.bindServiceAsUser(service, conn, flags, handler, user) }
+        return true
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
index bf48784..02a8141 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
@@ -69,6 +69,15 @@
     }
 
     @Test
+    fun testPassesIntentToStarter_dismissShadeAndShowOverLockScreenWhenLocked() {
+        val intent = Intent("test.ACTION")
+
+        underTest.handle(null, intent, true)
+
+        verify(activityStarter).startActivity(eq(intent), eq(true), any(), eq(true))
+    }
+
+    @Test
     fun testPassesActivityPendingIntentToStarterAsPendingIntent() {
         val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt
new file mode 100644
index 0000000..c41ce2f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.domain.interactor
+
+import android.content.Intent
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
+import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.Callback
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QRCodeScannerTileDataInteractorTest : SysuiTestCase() {
+
+    private val testUser = UserHandle.of(1)!!
+    private val testDispatcher = StandardTestDispatcher()
+    private val scope = TestScope(testDispatcher)
+    private val testIntent = mock<Intent>()
+    private val qrCodeScannerController =
+        mock<QRCodeScannerController> {
+            whenever(intent).thenReturn(testIntent)
+            whenever(isAbleToLaunchScannerActivity).thenReturn(false)
+        }
+    private val testAvailableModel = QRCodeScannerTileModel.Available(testIntent)
+    private val testUnavailableModel = QRCodeScannerTileModel.TemporarilyUnavailable
+
+    private val underTest: QRCodeScannerTileDataInteractor =
+        QRCodeScannerTileDataInteractor(
+            testDispatcher,
+            scope.backgroundScope,
+            qrCodeScannerController,
+        )
+
+    @Test
+    fun availability_matchesController_cameraNotAvailable() =
+        scope.runTest {
+            val expectedAvailability = false
+            whenever(qrCodeScannerController.isCameraAvailable).thenReturn(false)
+
+            val availability by collectLastValue(underTest.availability(testUser))
+
+            assertThat(availability).isEqualTo(expectedAvailability)
+        }
+
+    @Test
+    fun availability_matchesController_cameraIsAvailable() =
+        scope.runTest {
+            val expectedAvailability = true
+            whenever(qrCodeScannerController.isCameraAvailable).thenReturn(true)
+
+            val availability by collectLastValue(underTest.availability(testUser))
+
+            assertThat(availability).isEqualTo(expectedAvailability)
+        }
+
+    @Test
+    fun data_matchesController() =
+        scope.runTest {
+            val captor = argumentCaptor<Callback>()
+            val lastData by
+                collectLastValue(
+                    underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+                )
+            runCurrent()
+
+            verify(qrCodeScannerController).addCallback(captor.capture())
+            val callback = captor.value
+
+            assertThat(lastData!!).isEqualTo(testUnavailableModel)
+
+            whenever(qrCodeScannerController.isAbleToLaunchScannerActivity).thenReturn(true)
+            callback.onQRCodeScannerActivityChanged()
+            runCurrent()
+            assertThat(lastData!!).isEqualTo(testAvailableModel)
+
+            whenever(qrCodeScannerController.isAbleToLaunchScannerActivity).thenReturn(false)
+            callback.onQRCodeScannerActivityChanged()
+            runCurrent()
+            assertThat(lastData!!).isEqualTo(testUnavailableModel)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
new file mode 100644
index 0000000..312f180
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.domain.interactor
+
+import android.content.Intent
+import android.platform.test.annotations.EnabledOnRavenwood
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.qs.tiles.impl.qr.qrCodeScannerTileUserActionInteractor
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class QRCodeScannerTileUserActionInteractorTest : SysuiTestCase() {
+    val kosmos = Kosmos()
+    private val inputHandler = kosmos.qsTileIntentUserInputHandler
+    private val underTest = kosmos.qrCodeScannerTileUserActionInteractor
+    private val intent = mock<Intent>()
+
+    @Test
+    fun handleClick_available() = runTest {
+        val inputModel = QRCodeScannerTileModel.Available(intent)
+
+        underTest.handleInput(QSTileInputTestKtx.click(inputModel))
+
+        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+            intent
+        }
+    }
+
+    @Test
+    fun handleClick_temporarilyUnavailable() = runTest {
+        val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable
+
+        underTest.handleInput(QSTileInputTestKtx.click(inputModel))
+
+        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs()
+    }
+
+    @Test
+    fun handleLongClick_available() = runTest {
+        val inputModel = QRCodeScannerTileModel.Available(intent)
+
+        underTest.handleInput(QSTileInputTestKtx.longClick(inputModel))
+
+        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs()
+    }
+
+    @Test
+    fun handleLongClick_temporarilyUnavailable() = runTest {
+        val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable
+
+        underTest.handleInput(QSTileInputTestKtx.longClick(inputModel))
+
+        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
new file mode 100644
index 0000000..d26a213
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.ui
+
+import android.content.Intent
+import android.graphics.drawable.TestStubDrawable
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.qs.tiles.impl.qr.qsQRCodeScannerTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QRCodeScannerTileMapperTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val config = kosmos.qsQRCodeScannerTileConfig
+
+    private lateinit var mapper: QRCodeScannerTileMapper
+
+    @Before
+    fun setup() {
+        mapper =
+            QRCodeScannerTileMapper(
+                context.orCreateTestableResources
+                    .apply {
+                        addOverride(
+                            com.android.systemui.res.R.drawable.ic_qr_code_scanner,
+                            TestStubDrawable()
+                        )
+                    }
+                    .resources,
+                context.theme
+            )
+    }
+
+    @Test
+    fun availableModel() {
+        val mockIntent = mock<Intent>()
+        val inputModel = QRCodeScannerTileModel.Available(mockIntent)
+
+        val outputState = mapper.map(config, inputModel)
+
+        val expectedState =
+            createQRCodeScannerTileState(
+                QSTileState.ActivationState.INACTIVE,
+                null,
+            )
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun temporarilyUnavailableModel() {
+        val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable
+
+        val outputState = mapper.map(config, inputModel)
+
+        val expectedState =
+            createQRCodeScannerTileState(
+                QSTileState.ActivationState.UNAVAILABLE,
+                context.getString(
+                    com.android.systemui.res.R.string.qr_code_scanner_updating_secondary_label
+                )
+            )
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
+    private fun createQRCodeScannerTileState(
+        activationState: QSTileState.ActivationState,
+        secondaryLabel: String?,
+    ): QSTileState {
+        val label = context.getString(com.android.systemui.res.R.string.qr_code_scanner_title)
+        return QSTileState(
+            {
+                Icon.Loaded(
+                    context.getDrawable(com.android.systemui.res.R.drawable.ic_qr_code_scanner)!!,
+                    null
+                )
+            },
+            label,
+            activationState,
+            secondaryLabel,
+            setOf(QSTileState.UserAction.CLICK),
+            label,
+            null,
+            QSTileState.SideViewIcon.Chevron,
+            QSTileState.EnabledState.ENABLED,
+            Switch::class.qualifiedName
+        )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt
index c75e297..e3108ad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt
@@ -45,11 +45,11 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val sceneInteractor = kosmos.sceneInteractor
-    private val sceneContainerStartable = kosmos.sceneContainerStartable
-    private val authenticationInteractor = kosmos.authenticationInteractor
+    private val sceneInteractor by lazy { kosmos.sceneInteractor }
+    private val sceneContainerStartable by lazy { kosmos.sceneContainerStartable }
+    private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
 
-    private val underTest = kosmos.sceneBackInteractor
+    private val underTest by lazy { kosmos.sceneBackInteractor }
 
     @Test
     @EnableSceneContainer
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 93465c8..677477d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -437,12 +437,12 @@
             runCurrent()
             assertThat(
                     sysUiState.flags and
-                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED != 0
+                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED != 0L
                 )
                 .isTrue()
             assertThat(
                     sysUiState.flags and
-                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0
+                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0L
                 )
                 .isFalse()
 
@@ -450,12 +450,12 @@
             runCurrent()
             assertThat(
                     sysUiState.flags and
-                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED != 0
+                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED != 0L
                 )
                 .isFalse()
             assertThat(
                     sysUiState.flags and
-                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0
+                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0L
                 )
                 .isTrue()
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt
index a2ffe70..545a0c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.shared.model.TransitionKeys.GoneToSplitShade
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
@@ -67,7 +67,7 @@
             runCurrent()
 
             assertThat(destinationScenes?.get(Swipe(SwipeDirection.Down))?.transitionKey)
-                .isEqualTo(GoneToSplitShade)
+                .isEqualTo(ToSplitShade)
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt
index e11a8f1..851b7b9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt
@@ -52,9 +52,9 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
-    private val sceneInteractor = kosmos.sceneInteractor
-    private val shadeAnimationInteractor = kosmos.shadeAnimationInteractor
+    private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
+    private val sceneInteractor by lazy { kosmos.sceneInteractor }
+    private val shadeAnimationInteractor by lazy { kosmos.shadeAnimationInteractor }
     private val transitionState =
         MutableStateFlow<ObservableTransitionState>(
             ObservableTransitionState.Idle(Scenes.Lockscreen)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 4622f0c..482dc5d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -42,7 +42,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.GoneToSplitShade
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -169,11 +169,11 @@
             runCurrent()
 
             assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.transitionKey)
-                .isEqualTo(GoneToSplitShade)
+                .isEqualTo(ToSplitShade)
         }
 
     @Test
-    fun upTransitionKey_splitShadeDisabled_isNull() =
+    fun upTransitionKey_splitShadeDisable_isNull() =
         testScope.runTest {
             val destinationScenes by collectLastValue(underTest.destinationScenes)
             shadeRepository.setShadeMode(ShadeMode.Single)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
new file mode 100644
index 0000000..8cb811d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.service.notification.NotificationListenerService
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.mockNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifPipeline
+import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider
+import com.android.systemui.testKosmos
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationMediaManagerTest : SysuiTestCase() {
+
+    private val KEY = "KEY"
+
+    private val kosmos = testKosmos()
+    private val visibilityProvider = kosmos.notificationVisibilityProvider
+    private val notifPipeline = kosmos.notifPipeline
+    private val notifCollection = kosmos.mockNotifCollection
+    private val dumpManager = kosmos.dumpManager
+    private val mediaDataManager = mock<MediaDataManager>()
+    private val backgroundExecutor = FakeExecutor(FakeSystemClock())
+
+    private var listenerCaptor = argumentCaptor<MediaDataManager.Listener>()
+
+    private lateinit var notificationMediaManager: NotificationMediaManager
+
+    @Before
+    fun setup() {
+        notificationMediaManager =
+            NotificationMediaManager(
+                context,
+                visibilityProvider,
+                notifPipeline,
+                notifCollection,
+                mediaDataManager,
+                dumpManager,
+                backgroundExecutor,
+            )
+
+        verify(mediaDataManager).addListener(listenerCaptor.capture())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+    fun mediaDataRemoved_userInitiated_dismissNotif() {
+        val notifEntryCaptor = argumentCaptor<NotificationEntry>()
+        val notifEntry = mock<NotificationEntry>()
+        whenever(notifEntry.key).thenReturn(KEY)
+        whenever(notifEntry.ranking).thenReturn(NotificationListenerService.Ranking())
+        whenever(notifPipeline.allNotifs).thenReturn(listOf(notifEntry))
+
+        listenerCaptor.lastValue.onMediaDataRemoved(KEY, true)
+
+        verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any())
+        assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+    fun mediaDataRemoved_notUserInitiated_doesNotDismissNotif() {
+        listenerCaptor.lastValue.onMediaDataRemoved(KEY, false)
+
+        verify(notifCollection, never()).dismissNotification(any(), any())
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+    fun mediaDataRemoved_notUserInitiated_flagOff_dismissNotif() {
+        val notifEntryCaptor = argumentCaptor<NotificationEntry>()
+        val notifEntry = mock<NotificationEntry>()
+        whenever(notifEntry.key).thenReturn(KEY)
+        whenever(notifEntry.ranking).thenReturn(NotificationListenerService.Ranking())
+        whenever(notifPipeline.allNotifs).thenReturn(listOf(notifEntry))
+
+        listenerCaptor.lastValue.onMediaDataRemoved(KEY, false)
+
+        verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any())
+        assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
similarity index 62%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index c5d7e1f..2853264 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -23,9 +23,8 @@
 import android.app.NotificationManager.IMPORTANCE_LOW
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.notification.collection.GroupEntry
@@ -44,25 +43,29 @@
 import com.android.systemui.statusbar.notification.collection.render.NodeController
 import com.android.systemui.statusbar.notification.icon.ConversationIconManager
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifierImpl
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.withArgCaptor
 import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class ConversationCoordinatorTest : SysuiTestCase() {
     // captured listeners and pluggables:
@@ -72,22 +75,20 @@
     private lateinit var peopleComparator: NotifComparator
     private lateinit var beforeRenderListListener: OnBeforeRenderListListener
 
+    private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
+    private lateinit var peopleAlertingSection: NotifSection
+
     @Mock private lateinit var pipeline: NotifPipeline
     @Mock private lateinit var conversationIconManager: ConversationIconManager
-    @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
-    @Mock private lateinit var channel: NotificationChannel
     @Mock private lateinit var headerController: NodeController
-    private lateinit var entry: NotificationEntry
-    private lateinit var entryA: NotificationEntry
-    private lateinit var entryB: NotificationEntry
 
     private lateinit var coordinator: ConversationCoordinator
 
-    @Rule @JvmField public val setFlagsRule = SetFlagsRule()
-
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        peopleNotificationIdentifier =
+            PeopleNotificationIdentifierImpl(mock(), GroupMembershipManagerImpl())
         coordinator =
             ConversationCoordinator(
                 peopleNotificationIdentifier,
@@ -95,7 +96,6 @@
                 HighPriorityProvider(peopleNotificationIdentifier, GroupMembershipManagerImpl()),
                 headerController
             )
-        whenever(channel.isImportantConversation).thenReturn(true)
 
         coordinator.attach(pipeline)
 
@@ -110,49 +110,65 @@
         if (!SortBySectionTimeFlag.isEnabled)
             peopleComparator = peopleAlertingSectioner.comparator!!
 
-        entry = NotificationEntryBuilder().setChannel(channel).build()
+        peopleAlertingSection = NotifSection(peopleAlertingSectioner, 0)
+    }
 
-        val section = NotifSection(peopleAlertingSectioner, 0)
-        entryA =
-            NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("A").build()
-        entryB =
-            NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("B").build()
+    @Test
+    fun priorityPeopleSectionerClaimsOnlyImportantConversations() {
+        val sectioner = coordinator.priorityPeopleSectioner
+        assertTrue(sectioner.isInSection(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON)))
+        assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_FULL_PERSON)))
+        assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_PERSON)))
+        assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_NON_PERSON)))
+        assertFalse(sectioner.isInSection(NotificationEntryBuilder().build()))
     }
 
     @Test
     fun testPromotesImportantConversations() {
-        // only promote important conversations
-        assertTrue(promoter.shouldPromoteToTopLevel(entry))
+        assertTrue(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON)))
+        assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_FULL_PERSON)))
+        assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_PERSON)))
+        assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_NON_PERSON)))
         assertFalse(promoter.shouldPromoteToTopLevel(NotificationEntryBuilder().build()))
     }
 
     @Test
     fun testPromotedImportantConversationsMakesSummaryUnimportant() {
-        val altChildA = NotificationEntryBuilder().setTag("A").build()
-        val altChildB = NotificationEntryBuilder().setTag("B").build()
-        val summary = NotificationEntryBuilder().setId(2).setChannel(channel).build()
+        val importantChannel =
+            mock<NotificationChannel>().also {
+                whenever(it.isImportantConversation).thenReturn(true)
+            }
+        val otherChannel =
+            mock<NotificationChannel>().also {
+                whenever(it.isImportantConversation).thenReturn(false)
+            }
+        val importantChild =
+            makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setChannel(importantChannel) }
+        val altChildA =
+            makeEntryOfPeopleType(TYPE_FULL_PERSON) { setChannel(otherChannel).setTag("A") }
+        val altChildB =
+            makeEntryOfPeopleType(TYPE_FULL_PERSON) { setChannel(otherChannel).setTag("B") }
+        val summary =
+            makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setChannel(importantChannel).setId(2) }
         val groupEntry =
             GroupEntryBuilder()
                 .setParent(GroupEntry.ROOT_ENTRY)
                 .setSummary(summary)
-                .setChildren(listOf(entry, altChildA, altChildB))
+                .setChildren(listOf(importantChild, altChildA, altChildB))
                 .build()
-        assertTrue(promoter.shouldPromoteToTopLevel(entry))
+        assertTrue(promoter.shouldPromoteToTopLevel(importantChild))
         assertFalse(promoter.shouldPromoteToTopLevel(altChildA))
         assertFalse(promoter.shouldPromoteToTopLevel(altChildB))
-        NotificationEntryBuilder.setNewParent(entry, GroupEntry.ROOT_ENTRY)
-        GroupEntryBuilder.getRawChildren(groupEntry).remove(entry)
-        beforeRenderListListener.onBeforeRenderList(listOf(entry, groupEntry))
+        NotificationEntryBuilder.setNewParent(importantChild, GroupEntry.ROOT_ENTRY)
+        GroupEntryBuilder.getRawChildren(groupEntry).remove(importantChild)
+        beforeRenderListListener.onBeforeRenderList(listOf(importantChild, groupEntry))
         verify(conversationIconManager).setUnimportantConversations(eq(listOf(summary.key)))
     }
 
     @Test
     fun testInAlertingPeopleSectionWhenTheImportanceIsAtLeastDefault() {
         // GIVEN
-        val alertingEntry =
-            NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_DEFAULT).build()
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(alertingEntry))
-            .thenReturn(TYPE_PERSON)
+        val alertingEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_DEFAULT) }
 
         // put alerting people notifications in this section
         assertThat(peopleAlertingSectioner.isInSection(alertingEntry)).isTrue()
@@ -162,10 +178,7 @@
     @EnableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
     fun testInAlertingPeopleSectionWhenTheImportanceIsLowerThanDefault() {
         // GIVEN
-        val silentEntry =
-                NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build()
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry))
-                .thenReturn(TYPE_PERSON)
+        val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) }
 
         // THEN put silent people notifications in alerting section
         assertThat(peopleAlertingSectioner.isInSection(silentEntry)).isTrue()
@@ -175,10 +188,7 @@
     @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
     fun testInSilentPeopleSectionWhenTheImportanceIsLowerThanDefault() {
         // GIVEN
-        val silentEntry =
-            NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build()
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry))
-            .thenReturn(TYPE_PERSON)
+        val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) }
 
         // THEN put silent people notifications in this section
         assertThat(peopleSilentSectioner.isInSection(silentEntry)).isTrue()
@@ -191,18 +201,14 @@
     @Test
     fun testNotInPeopleSection() {
         // GIVEN
-        val entry =
-            NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build()
+        val entry = makeEntryOfPeopleType(TYPE_NON_PERSON) { setImportance(IMPORTANCE_LOW) }
         val importantEntry =
-            NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_HIGH).build()
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entry))
-            .thenReturn(TYPE_NON_PERSON)
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(importantEntry))
-            .thenReturn(TYPE_NON_PERSON)
+            makeEntryOfPeopleType(TYPE_NON_PERSON) { setImportance(IMPORTANCE_HIGH) }
 
         // THEN - only put people notification either silent or alerting
-        if (!SortBySectionTimeFlag.isEnabled)
+        if (!SortBySectionTimeFlag.isEnabled) {
             assertThat(peopleSilentSectioner.isInSection(entry)).isFalse()
+        }
         assertThat(peopleAlertingSectioner.isInSection(importantEntry)).isFalse()
     }
 
@@ -210,22 +216,16 @@
     fun testInAlertingPeopleSectionWhenThereIsAnImportantChild() {
         // GIVEN
         val altChildA =
-            NotificationEntryBuilder().setTag("A").setImportance(IMPORTANCE_DEFAULT).build()
-        val altChildB = NotificationEntryBuilder().setTag("B").setImportance(IMPORTANCE_LOW).build()
-        val summary =
-            NotificationEntryBuilder()
-                .setId(2)
-                .setImportance(IMPORTANCE_LOW)
-                .setChannel(channel)
-                .build()
+            makeEntryOfPeopleType(TYPE_NON_PERSON) { setTag("A").setImportance(IMPORTANCE_DEFAULT) }
+        val altChildB =
+            makeEntryOfPeopleType(TYPE_NON_PERSON) { setTag("B").setImportance(IMPORTANCE_LOW) }
+        val summary = makeEntryOfPeopleType(TYPE_PERSON) { setId(2).setImportance(IMPORTANCE_LOW) }
         val groupEntry =
             GroupEntryBuilder()
                 .setParent(GroupEntry.ROOT_ENTRY)
                 .setSummary(summary)
                 .setChildren(listOf(altChildA, altChildB))
                 .build()
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(summary))
-            .thenReturn(TYPE_PERSON)
         // THEN
         assertThat(peopleAlertingSectioner.isInSection(groupEntry)).isTrue()
     }
@@ -233,10 +233,12 @@
     @Test
     @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
     fun testComparatorPutsImportantPeopleFirst() {
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA))
-            .thenReturn(TYPE_IMPORTANT_PERSON)
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB))
-            .thenReturn(TYPE_PERSON)
+        val entryA =
+            makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) {
+                setSection(peopleAlertingSection).setTag("A")
+            }
+        val entryB =
+            makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("B") }
 
         // only put people notifications in this section
         assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(-1)
@@ -245,10 +247,10 @@
     @Test
     @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
     fun testComparatorEquatesPeopleWithSameType() {
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA))
-            .thenReturn(TYPE_PERSON)
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB))
-            .thenReturn(TYPE_PERSON)
+        val entryA =
+            makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("A") }
+        val entryB =
+            makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("B") }
 
         // only put people notifications in this section
         assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(0)
@@ -259,4 +261,23 @@
     fun testNoSecondarySortForConversations() {
         assertThat(peopleAlertingSectioner.comparator).isNull()
     }
+
+    private fun makeEntryOfPeopleType(
+        @PeopleNotificationType type: Int,
+        buildBlock: NotificationEntryBuilder.() -> Unit = {}
+    ): NotificationEntry {
+        val channel: NotificationChannel = mock()
+        whenever(channel.isImportantConversation).thenReturn(type == TYPE_IMPORTANT_PERSON)
+        val entry =
+            NotificationEntryBuilder()
+                .updateRanking {
+                    it.setIsConversation(type != TYPE_NON_PERSON)
+                    it.setShortcutInfo(if (type >= TYPE_FULL_PERSON) mock() else null)
+                    it.setChannel(channel)
+                }
+                .also(buildBlock)
+                .build()
+        assertEquals(type, peopleNotificationIdentifier.getPeopleNotificationType(entry))
+        return entry
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 50ae985..ce134e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -31,9 +31,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.compose.animation.scene.ObservableTransitionState;
@@ -57,6 +57,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.kotlin.JavaAdapter;
@@ -75,7 +76,7 @@
 import org.mockito.verification.VerificationMode;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class VisualStabilityCoordinatorTest extends SysuiTestCase {
 
@@ -86,6 +87,7 @@
     @Mock private WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener;
+    @Mock private SeenNotificationsInteractor mSeenNotificationsInteractor;
     @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private VisibilityLocationProvider mVisibilityLocationProvider;
     @Mock private VisualStabilityProvider mVisualStabilityProvider;
@@ -121,6 +123,7 @@
                 mHeadsUpManager,
                 mShadeAnimationInteractor,
                 mJavaAdapter,
+                mSeenNotificationsInteractor,
                 mStatusBarStateController,
                 mVisibilityLocationProvider,
                 mVisualStabilityProvider,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
index 1f0812d..ee9fd349 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
@@ -44,13 +44,4 @@
                 collectLastValue(kosmos.notificationStackAppearanceInteractor.shadeScrimBounds)
             assertThat(stackBounds).isEqualTo(bounds)
         }
-
-    @Test
-    fun onStackBoundsChanged() =
-        kosmos.testScope.runTest {
-            underTest.onStackBoundsChanged(top = 5f, bottom = 500f)
-            assertThat(kosmos.notificationStackAppearanceInteractor.stackTop.value).isEqualTo(5f)
-            assertThat(kosmos.notificationStackAppearanceInteractor.stackBottom.value)
-                .isEqualTo(500f)
-        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index f2ce745..82e2bb7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags
@@ -107,18 +108,25 @@
     val testScope = kosmos.testScope
     val configurationRepository
         get() = kosmos.fakeConfigurationRepository
+
     val keyguardRepository
         get() = kosmos.fakeKeyguardRepository
+
     val keyguardInteractor
         get() = kosmos.keyguardInteractor
+
     val keyguardRootViewModel
         get() = kosmos.keyguardRootViewModel
+
     val keyguardTransitionRepository
         get() = kosmos.fakeKeyguardTransitionRepository
+
     val shadeTestUtil
         get() = kosmos.shadeTestUtil
+
     val sharedNotificationContainerInteractor
         get() = kosmos.sharedNotificationContainerInteractor
+
     val largeScreenHeaderHelper
         get() = kosmos.mockLargeScreenHeaderHelper
 
@@ -660,9 +668,6 @@
 
             overrideResource(R.bool.config_use_split_notification_shade, false)
             configurationRepository.onAnyConfigurationChange()
-            keyguardInteractor.setNotificationContainerBounds(
-                NotificationContainerBounds(top = 1f, bottom = 2f)
-            )
 
             assertThat(maxNotifications).isEqualTo(10)
 
@@ -691,9 +696,6 @@
 
             overrideResource(R.bool.config_use_split_notification_shade, false)
             configurationRepository.onAnyConfigurationChange()
-            keyguardInteractor.setNotificationContainerBounds(
-                NotificationContainerBounds(top = 1f, bottom = 2f)
-            )
 
             assertThat(maxNotifications).isEqualTo(10)
 
@@ -728,9 +730,6 @@
 
             overrideResource(R.bool.config_use_split_notification_shade, false)
             configurationRepository.onAnyConfigurationChange()
-            keyguardInteractor.setNotificationContainerBounds(
-                NotificationContainerBounds(top = 1f, bottom = 2f)
-            )
 
             // -1 means No Limit
             assertThat(maxNotifications).isEqualTo(-1)
@@ -823,6 +822,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(330311871)
     fun alphaDoesNotUpdateWhileGoneTransitionIsRunning() =
         testScope.runTest {
             val viewState = ViewStateAccessor()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostCoroutinesTest.kt
index 1cd12f0..7bc6948 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostCoroutinesTest.kt
@@ -35,6 +35,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.verify
@@ -46,30 +47,32 @@
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
-    private val sceneContainerRepository = kosmos.sceneContainerRepository
-    private val keyguardInteractor = kosmos.keyguardInteractor
+    lateinit var underTest: DozeServiceHost
 
-    val underTest =
-        kosmos.dozeServiceHost.apply {
-            initialize(
-                /* centralSurfaces = */ mock(),
-                /* statusBarKeyguardViewManager = */ mock(),
-                /* notificationShadeWindowViewController = */ mock(),
-                /* ambientIndicationContainer = */ mock(),
-            )
-        }
+    @Before
+    fun setup() {
+        underTest =
+            kosmos.dozeServiceHost.apply {
+                initialize(
+                    /* centralSurfaces = */ mock(),
+                    /* statusBarKeyguardViewManager = */ mock(),
+                    /* notificationShadeWindowViewController = */ mock(),
+                    /* ambientIndicationContainer = */ mock(),
+                )
+            }
+    }
 
     @Test
     @EnableSceneContainer
     fun startStopDozing() =
         testScope.runTest {
-            val isDozing by collectLastValue(keyguardInteractor.isDozing)
+            val isDozing by collectLastValue(kosmos.keyguardInteractor.isDozing)
 
             // GIVEN a callback is set
             val callback: DozeHost.Callback = mock()
             underTest.addCallback(callback)
             // AND we are on the lock screen
-            sceneContainerRepository.changeScene(Scenes.Lockscreen)
+            kosmos.sceneContainerRepository.changeScene(Scenes.Lockscreen)
             // AND dozing is not requested yet
             assertThat(underTest.dozingRequested).isFalse()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
index 675136c..a163ca0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
@@ -36,7 +36,6 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -47,19 +46,8 @@
 
     private val kosmos = testKosmos()
 
-    private lateinit var underTest: AudioVolumeInteractor
-
-    @Before
-    fun setup() {
-        with(kosmos) {
-            underTest = AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor)
-
-            audioRepository.setRingerMode(RingerMode(AudioManager.RINGER_MODE_NORMAL))
-
-            notificationsSoundPolicyRepository.updateNotificationPolicy()
-            notificationsSoundPolicyRepository.updateZenMode(ZenMode(Settings.Global.ZEN_MODE_OFF))
-        }
-    }
+    private val underTest: AudioVolumeInteractor =
+        with(kosmos) { AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor) }
 
     @Test
     fun setMuted_mutesStream() {
@@ -236,6 +224,55 @@
         }
     }
 
+    @Test
+    fun testReducingVolumeToMin_mutes() =
+        with(kosmos) {
+            testScope.runTest {
+                val audioStreamModel by
+                    collectLastValue(audioRepository.getAudioStream(audioStream))
+                runCurrent()
+
+                underTest.setVolume(audioStream, audioStreamModel!!.minVolume)
+                runCurrent()
+
+                assertThat(audioStreamModel!!.isMuted).isTrue()
+            }
+        }
+
+    @Test
+    fun testIncreasingVolumeFromMin_unmutes() =
+        with(kosmos) {
+            testScope.runTest {
+                val audioStreamModel by
+                    collectLastValue(audioRepository.getAudioStream(audioStream))
+                audioRepository.setMuted(audioStream, true)
+                audioRepository.setVolume(audioStream, audioStreamModel!!.minVolume)
+                runCurrent()
+
+                underTest.setVolume(audioStream, audioStreamModel!!.maxVolume)
+                runCurrent()
+
+                assertThat(audioStreamModel!!.isMuted).isFalse()
+            }
+        }
+
+    @Test
+    fun testUnmutingMinVolume_increasesVolume() =
+        with(kosmos) {
+            testScope.runTest {
+                val audioStreamModel by
+                    collectLastValue(audioRepository.getAudioStream(audioStream))
+                audioRepository.setMuted(audioStream, true)
+                audioRepository.setVolume(audioStream, audioStreamModel!!.minVolume)
+                runCurrent()
+
+                underTest.setMuted(audioStream, false)
+                runCurrent()
+
+                assertThat(audioStreamModel!!.volume).isGreaterThan(audioStreamModel!!.minVolume)
+            }
+        }
+
     private companion object {
         val audioStream = AudioStream(AudioManager.STREAM_SYSTEM)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
index 64c9429..46df0c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
@@ -16,17 +16,16 @@
 
 package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
 
-import android.os.Handler
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
 import com.android.systemui.volume.localMediaController
 import com.android.systemui.volume.mediaControllerRepository
+import com.android.systemui.volume.mediaDeviceSessionInteractor
 import com.android.systemui.volume.mediaOutputInteractor
 import com.android.systemui.volume.panel.shared.model.filterData
 import com.android.systemui.volume.remoteMediaController
@@ -55,12 +54,7 @@
                 listOf(localMediaController, remoteMediaController)
             )
 
-            underTest =
-                MediaDeviceSessionInteractor(
-                    testScope.testScheduler,
-                    Handler(TestableLooper.get(kosmos.testCase).looper),
-                    mediaControllerRepository,
-                )
+            underTest = mediaDeviceSessionInteractor
         }
     }
 
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 79bf5f1..629c96c 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
@@ -109,6 +109,12 @@
     val largeClockMessageBuffer: MessageBuffer,
 )
 
+data class AodClockBurnInModel(
+    val scale: Float,
+    val translationX: Float,
+    val translationY: Float,
+)
+
 /** Specifies layout information for the */
 interface ClockFaceLayout {
     /** All clock views to add to the root constraint layout before applying constraints. */
@@ -118,6 +124,8 @@
     fun applyConstraints(constraints: ConstraintSet): ConstraintSet
 
     fun applyPreviewConstraints(constraints: ConstraintSet): ConstraintSet
+
+    fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel)
 }
 
 /** A ClockFaceLayout that applies the default lockscreen layout to a single view */
@@ -137,6 +145,10 @@
     override fun applyPreviewConstraints(constraints: ConstraintSet): ConstraintSet {
         return constraints
     }
+
+    override fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) {
+        // Default clock doesn't need detailed control of view
+    }
 }
 
 /** Events that should call when various rendering parameters change */
diff --git a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
index a751f58..370677ac 100644
--- a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
+++ b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
@@ -16,5 +16,5 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
     <solid android:color="@color/material_dynamic_neutral20" />
-    <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
+    <corners android:radius="@dimen/ongoing_activity_chip_corner_radius" />
 </shape>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index b4b2a19..efc750b 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -61,7 +61,7 @@
     <string name="bouncer_face_not_recognized" msgid="1666128054475597485">"Lice nije prepoznato"</string>
     <string name="kg_bio_try_again_or_pin" msgid="4752168242723808390">"Pokušajte ponovno ili unesite PIN"</string>
     <string name="kg_bio_try_again_or_password" msgid="1473132729225398039">"Pokušajte ponovno ili unesite zaporku"</string>
-    <string name="kg_bio_try_again_or_pattern" msgid="4867893307468801501">"Pokušajte ponovno ili izradite uzorak"</string>
+    <string name="kg_bio_try_again_or_pattern" msgid="4867893307468801501">"Pokušajte ponovno ili nacrtajte uzorak"</string>
     <string name="kg_bio_too_many_attempts_pin" msgid="5850845723433047605">"PIN je obavezan nakon previše pokušaja"</string>
     <string name="kg_bio_too_many_attempts_password" msgid="5551690347827728042">"Zaporka je obavezna nakon previše pokušaja"</string>
     <string name="kg_bio_too_many_attempts_pattern" msgid="736884689355181602">"Uzorak je obavezan nakon previše pokušaja"</string>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index c4fc5f7..2bace4a 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -116,7 +116,7 @@
     <string name="kg_prompt_added_security_password" msgid="6053156069765029006">"ထပ်ဆောင်း လုံခြုံရေးအတွက် စကားဝှက် လိုအပ်သည်"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"စက်ပစ္စည်းကို စီမံခန့်ခွဲသူက လော့ခ်ချထားပါသည်"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"စက်ပစ္စည်းကို ကိုယ်တိုင်ကိုယ်ကျ လော့ခ်ချထားခဲ့သည်"</string>
-    <string name="kg_face_not_recognized" msgid="7903950626744419160">"မသိ"</string>
+    <string name="kg_face_not_recognized" msgid="7903950626744419160">"မသိပါ"</string>
     <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"‘မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း’ သုံးရန် ‘ဆက်တင်များ’ တွင်ကင်မရာသုံးခွင့်ကိုဖွင့်ပါ"</string>
     <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{ဆင်းမ်ပင်နံပါတ် ထည့်သွင်းပါ။ သင့်စက်ကို လော့ခ်ဖွင့်ရန် မိုဘိုင်းဖုန်းကုမ္ပဏီသို့ မဆက်သွယ်မီ # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။}other{ဆင်းမ်ပင်နံပါတ် ထည့်သွင်းပါ။ သင့်တွင် # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။}}"</string>
     <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{ဆင်းမ်ကတ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် PUK ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကတ် အပြီးပိတ်မသွားမီ သင့်တွင် # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။ အသေးစိတ်အတွက် မိုဘိုင်းဖုန်းကုမ္ပဏီကို ဆက်သွယ်ပါ။}other{ဆင်းမ်ကတ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် PUK ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကတ် အပြီးပိတ်မသွားမီ သင့်တွင် # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။ အသေးစိတ်အတွက် မိုဘိုင်းဖုန်းကုမ္ပဏီကို ဆက်သွယ်ပါ။}}"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 2bfdef6..224f1ae 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -58,7 +58,7 @@
     <string name="kg_wrong_pin_try_again" msgid="3129729383303430190">"PIN मिलेन। फेरि प्रयास गर्नुहोस्।"</string>
     <string name="kg_wrong_input_try_fp_suggestion" msgid="3143861542242024833">"वा फिंगरप्रिन्ट प्रयोग गरी अनलक गर्नुहोस्"</string>
     <string name="kg_fp_not_recognized" msgid="5183108260932029241">"फिंगरप्रिन्ट मिलेन"</string>
-    <string name="bouncer_face_not_recognized" msgid="1666128054475597485">"अनुहार पहिचान गर्न सकिएन"</string>
+    <string name="bouncer_face_not_recognized" msgid="1666128054475597485">"अनुहार मिलेन"</string>
     <string name="kg_bio_try_again_or_pin" msgid="4752168242723808390">"फेरि प्रयास गर्नुहोस् वा PIN हाल्नुहोस्"</string>
     <string name="kg_bio_try_again_or_password" msgid="1473132729225398039">"फेरि प्रयास गर्नुहोस् वा पासवर्ड हाल्नुहोस्"</string>
     <string name="kg_bio_try_again_or_pattern" msgid="4867893307468801501">"फेरि प्रयास गर्नुहोस् वा प्याटर्न हाल्नुहोस्"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index 9b0647a..4dffd97 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -57,7 +57,7 @@
     <string name="kg_wrong_pin" msgid="4160978845968732624">"Nesprávny kód PIN"</string>
     <string name="kg_wrong_pin_try_again" msgid="3129729383303430190">"Nesprávny kód PIN. Zopakujte."</string>
     <string name="kg_wrong_input_try_fp_suggestion" msgid="3143861542242024833">"Alebo odomknite odtlačkom prsta"</string>
-    <string name="kg_fp_not_recognized" msgid="5183108260932029241">"Nerozpoz. odtlačok prsta"</string>
+    <string name="kg_fp_not_recognized" msgid="5183108260932029241">"Odtlačok prsta nebol rozpoznaný"</string>
     <string name="bouncer_face_not_recognized" msgid="1666128054475597485">"Tvár nebola rozpoznaná"</string>
     <string name="kg_bio_try_again_or_pin" msgid="4752168242723808390">"Skúste to znova alebo zadajte PIN"</string>
     <string name="kg_bio_try_again_or_password" msgid="1473132729225398039">"Skúste to znova alebo zadajte heslo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index 60f60c4..b73372f7 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -57,7 +57,7 @@
     <string name="kg_wrong_pin" msgid="4160978845968732624">"தவறான பின்"</string>
     <string name="kg_wrong_pin_try_again" msgid="3129729383303430190">"தவறு. மீண்டும் முயலவும்."</string>
     <string name="kg_wrong_input_try_fp_suggestion" msgid="3143861542242024833">"இல்லையெனில் கைரேகை மூலம் அன்லாக் செய்யவும்"</string>
-    <string name="kg_fp_not_recognized" msgid="5183108260932029241">"கைரேகை அடையாளம் இல்லை"</string>
+    <string name="kg_fp_not_recognized" msgid="5183108260932029241">"கைரேகையை அடையாளம் காண முடியவில்லை"</string>
     <string name="bouncer_face_not_recognized" msgid="1666128054475597485">"முகம் கண்டறிய முடியவில்லை"</string>
     <string name="kg_bio_try_again_or_pin" msgid="4752168242723808390">"மீண்டும் முயலவும் அல்லது பின்னை உள்ளிடவும்"</string>
     <string name="kg_bio_try_again_or_password" msgid="1473132729225398039">"மீண்டும் முயலவும் அல்லது கடவுச்சொல்லை உள்ளிடவும்"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index fa2a234..fcb3a3e 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -116,7 +116,7 @@
     <string name="kg_prompt_added_security_password" msgid="6053156069765029006">"اضافی سیکیورٹی کیلئے پاس ورڈ درکار ہے"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"آلہ منتظم کی جانب سے مقفل ہے"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"آلہ کو دستی طور پر مقفل کیا گیا تھا"</string>
-    <string name="kg_face_not_recognized" msgid="7903950626744419160">"تسلیم شدہ نہیں ہے"</string>
+    <string name="kg_face_not_recognized" msgid="7903950626744419160">"شناخت نہیں ہو سکی"</string>
     <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"فیس اَنلاک استعمال کرنے کیلئے، ترتیبات میں کیمرا تک رسائی کو آن کریں"</string>
     <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{‏SIM کا PIN درج کریں۔ اس سے پہلے کہ آپ اپنا آلہ غیر مقفل کرنے کیلئے لازمی طور پر اپنے کیریئر سے رابطہ کریں آپ کے پاس # کوشش بچی ہے۔}other{‏SIM کا PIN درج کریں۔ آپ کے پاس # کوششیں بچی ہیں۔}}"</string>
     <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{‏SIM اب غیر فعال ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس # کوشش بچی ہے۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔}other{‏SIM اب غیر فعال ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس # کوششیں بچی ہیں۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔}}"</string>
diff --git a/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml b/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml
deleted file mode 100644
index 4181220..0000000
--- a/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright 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.
-*/
--->
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-
-    <item android:state_selected="true">
-        <shape android:shape="rectangle">
-            <solid android:color="?androidprv:attr/colorSurfaceHighlight" />
-            <stroke
-                android:color="?androidprv:attr/colorAccentPrimary"
-                android:width="@dimen/contrast_dialog_button_stroke_width" />
-            <corners android:radius="@dimen/contrast_dialog_button_radius"/>
-        </shape>
-    </item>
-
-    <item>
-        <layer-list>
-            <item android:top="@dimen/contrast_dialog_button_stroke_width"
-                android:bottom="@dimen/contrast_dialog_button_stroke_width"
-                android:left="@dimen/contrast_dialog_button_stroke_width"
-                android:right="@dimen/contrast_dialog_button_stroke_width">
-                <shape android:shape="rectangle">
-                    <solid android:color="?androidprv:attr/colorSurfaceHighlight" />
-                    <corners android:radius="@dimen/contrast_dialog_button_radius"/>
-                </shape>
-            </item>
-        </layer-list>
-    </item>
-</selector>
diff --git a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml b/packages/SystemUI/res/drawable/hub_handle.xml
similarity index 77%
copy from packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
copy to packages/SystemUI/res/drawable/hub_handle.xml
index bdd6270..8bc276f 100644
--- a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/hub_handle.xml
@@ -1,5 +1,5 @@
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -15,6 +15,6 @@
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="?android:attr/colorAccent" />
-    <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
+    <corners android:radius="4dp" />
+    <solid android:color="#FFFFFF" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_contrast_high.xml b/packages/SystemUI/res/drawable/ic_contrast_high.xml
deleted file mode 100644
index aa5b5ab..0000000
--- a/packages/SystemUI/res/drawable/ic_contrast_high.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  ~ Copyright (C) 2023 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<vector android:autoMirrored="true" android:height="20dp"
-    android:viewportHeight="20" android:viewportWidth="66"
-    android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#F2F1E8"
-        android:pathData="M0.5,8C0.5,3.858 3.858,0.5 8,0.5H58C62.142,0.5 65.5,3.858 65.5,8V12C65.5,16.142 62.142,19.5 58,19.5H8C3.858,19.5 0.5,16.142 0.5,12V8Z"
-        android:strokeColor="#1B1C17" android:strokeWidth="1"/>
-    <path android:fillColor="#1B1C17" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
-    <path android:fillColor="#1B1C17" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/>
-    <path android:fillColor="#1B1C17" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_contrast_medium.xml b/packages/SystemUI/res/drawable/ic_contrast_medium.xml
deleted file mode 100644
index 89519b8..0000000
--- a/packages/SystemUI/res/drawable/ic_contrast_medium.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
-  ~ Copyright (C) 2023 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<vector android:autoMirrored="true" android:height="20dp"
-    android:viewportHeight="20" android:viewportWidth="66"
-    android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#F2F1E8" android:pathData="M0,8C0,3.582 3.582,0 8,0H58C62.418,0 66,3.582 66,8V12C66,16.418 62.418,20 58,20H8C3.582,20 0,16.418 0,12V8Z"/>
-    <path android:fillColor="#919283" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
-    <path android:fillColor="#919283" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/>
-    <path android:fillColor="#919283" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_contrast_standard.xml b/packages/SystemUI/res/drawable/ic_contrast_standard.xml
deleted file mode 100644
index f914975..0000000
--- a/packages/SystemUI/res/drawable/ic_contrast_standard.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
-  ~ Copyright (C) 2023 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<vector android:autoMirrored="true" android:height="20dp"
-    android:viewportHeight="20" android:viewportWidth="66"
-    android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#C7C8B7" android:pathData="M0,8C0,3.582 3.582,0 8,0H58C62.418,0 66,3.582 66,8V12C66,16.418 62.418,20 58,20H8C3.582,20 0,16.418 0,12V8Z"/>
-    <path android:fillColor="#919283" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
-    <path android:fillColor="#919283" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/>
-    <path android:fillColor="#919283" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml b/packages/SystemUI/res/drawable/ongoing_activity_chip_bg.xml
similarity index 90%
rename from packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
rename to packages/SystemUI/res/drawable/ongoing_activity_chip_bg.xml
index bdd6270..b9a4cbf 100644
--- a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/ongoing_activity_chip_bg.xml
@@ -16,5 +16,5 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
     <solid android:color="?android:attr/colorAccent" />
-    <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
+    <corners android:radius="@dimen/ongoing_activity_chip_corner_radius" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
index f644584f..01b9f7e 100644
--- a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
@@ -26,8 +26,8 @@
         android:paddingVertical="16dp"
         android:visibility="visible"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
-        app:layout_constraintStart_toStartOf="@+id/leftGuideline"
+        app:layout_constraintRight_toLeftOf="@+id/rightGuideline"
+        app:layout_constraintLeft_toLeftOf="@+id/leftGuideline"
         app:layout_constraintTop_toTopOf="@+id/topGuideline" />
 
     <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
@@ -35,8 +35,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintTop_toTopOf="parent"
         tools:srcCompat="@tools:sample/avatars" />
 
@@ -47,6 +47,7 @@
         android:layout_gravity="center"
         android:contentDescription="@null"
         android:scaleType="fitXY"
+        android:importantForAccessibility="no"
         app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
         app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
         app:layout_constraintStart_toStartOf="@+id/biometric_icon"
@@ -62,8 +63,8 @@
         android:paddingTop="24dp"
         android:fadeScrollbars="false"
         app:layout_constraintBottom_toTopOf="@+id/button_bar"
-        app:layout_constraintEnd_toStartOf="@+id/midGuideline"
-        app:layout_constraintStart_toStartOf="@id/leftGuideline"
+        app:layout_constraintRight_toLeftOf="@+id/midGuideline"
+        app:layout_constraintLeft_toLeftOf="@id/leftGuideline"
         app:layout_constraintTop_toTopOf="@+id/topGuideline">
 
         <androidx.constraintlayout.widget.ConstraintLayout
@@ -88,7 +89,7 @@
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:textAlignment="viewStart"
-                android:paddingLeft="16dp"
+                android:paddingStart="16dp"
                 app:layout_constraintBottom_toBottomOf="@+id/logo"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toEndOf="@+id/logo"
@@ -208,6 +209,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
+        app:guidelineUseRtl="false"
         app:layout_constraintGuide_begin="@dimen/biometric_dialog_border_padding" />
 
     <androidx.constraintlayout.widget.Guideline
@@ -215,6 +217,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
+        app:guidelineUseRtl="false"
         app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
 
     <androidx.constraintlayout.widget.Guideline
@@ -222,6 +225,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
+        app:guidelineUseRtl="false"
         app:layout_constraintGuide_begin="406dp" />
 
     <androidx.constraintlayout.widget.Guideline
diff --git a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
index 46b8e46..05f6fae 100644
--- a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
@@ -229,6 +229,7 @@
         android:layout_gravity="center"
         android:contentDescription="@null"
         android:scaleType="fitXY"
+        android:importantForAccessibility="no"
         app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
         app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
         app:layout_constraintStart_toStartOf="@+id/biometric_icon"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml
index 4d2310a..0bbe73c 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml
@@ -27,7 +27,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
-        android:layout_marginLeft="24dp"
+        android:layout_marginStart="24dp"
         android:ellipsize="end"
         android:maxLines="2"
         android:visibility="invisible"
@@ -41,7 +41,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
-        android:layout_marginLeft="24dp"
+        android:layout_marginStart="24dp"
         android:text="@string/cancel"
         android:visibility="invisible"
         app:layout_constraintBottom_toBottomOf="parent"
@@ -54,7 +54,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
-        android:layout_marginLeft="24dp"
+        android:layout_marginStart="24dp"
         android:visibility="invisible"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent" />
@@ -66,7 +66,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
-        android:layout_marginRight="24dp"
+        android:layout_marginEnd="24dp"
         android:ellipsize="end"
         android:maxLines="2"
         android:text="@string/biometric_dialog_confirm"
@@ -81,7 +81,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
-        android:layout_marginRight="24dp"
+        android:layout_marginEnd="24dp"
         android:ellipsize="end"
         android:maxLines="2"
         android:text="@string/biometric_dialog_try_again"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
index d51fe58..fa4d9a8 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
@@ -222,6 +222,7 @@
         android:layout_gravity="center"
         android:contentDescription="@null"
         android:scaleType="fitXY"
+        android:importantForAccessibility="no"
         app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
         app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
         app:layout_constraintStart_toStartOf="@+id/biometric_icon"
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index 76d10cc..a598007 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -136,7 +136,8 @@
             <TextView
                 android:id="@+id/bluetooth_auto_on_toggle_title"
                 android:layout_width="0dp"
-                android:layout_height="68dp"
+                android:layout_height="wrap_content"
+                android:minHeight="68dp"
                 android:layout_marginBottom="20dp"
                 android:maxLines="2"
                 android:ellipsize="end"
diff --git a/packages/SystemUI/res/layout/clipboard_overlay2.xml b/packages/SystemUI/res/layout/clipboard_overlay2.xml
index 33ad2cd..521369e 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay2.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay2.xml
@@ -31,7 +31,7 @@
         android:layout_width="0dp"
         android:elevation="4dp"
         android:background="@drawable/shelf_action_chip_container_background"
-        android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
+        android:layout_marginStart="@dimen/overlay_action_container_minimum_edge_spacing"
         android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="@+id/actions_container"
diff --git a/packages/SystemUI/res/layout/contrast_dialog.xml b/packages/SystemUI/res/layout/contrast_dialog.xml
deleted file mode 100644
index 8e885cf..0000000
--- a/packages/SystemUI/res/layout/contrast_dialog.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  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.
-  -->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="horizontal">
-
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"/>
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <FrameLayout
-            android:id="@+id/contrast_button_standard"
-            android:layout_width="@dimen/contrast_dialog_button_total_size"
-            android:layout_height="@dimen/contrast_dialog_button_total_size"
-            android:background="@drawable/contrast_dialog_button_background">
-
-            <ImageView
-                android:layout_gravity="center"
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                android:src="@drawable/ic_contrast_standard"/>
-        </FrameLayout>
-
-        <TextView
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing"
-            android:gravity="center_horizontal|top"
-            android:textSize="@dimen/contrast_dialog_button_text_size"
-            android:text="@string/quick_settings_contrast_standard"
-            android:textColor="?androidprv:attr/textColorPrimary"/>
-    </LinearLayout>
-
-    <Space
-        android:layout_width="@dimen/contrast_dialog_button_horizontal_spacing"
-        android:layout_height="match_parent" />
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <FrameLayout
-            android:id="@+id/contrast_button_medium"
-            android:layout_width="@dimen/contrast_dialog_button_total_size"
-            android:layout_height="@dimen/contrast_dialog_button_total_size"
-            android:background="@drawable/contrast_dialog_button_background">
-
-            <ImageView
-                android:layout_gravity="center"
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                android:src="@drawable/ic_contrast_medium"/>
-        </FrameLayout>
-
-        <TextView
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing"
-            android:gravity="center_horizontal|top"
-            android:textSize="@dimen/contrast_dialog_button_text_size"
-            android:text="@string/quick_settings_contrast_medium"
-            android:textColor="?androidprv:attr/textColorPrimary"/>
-    </LinearLayout>
-
-    <Space
-        android:layout_width="@dimen/contrast_dialog_button_horizontal_spacing"
-        android:layout_height="match_parent" />
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <FrameLayout
-            android:id="@+id/contrast_button_high"
-            android:layout_width="@dimen/contrast_dialog_button_total_size"
-            android:layout_height="@dimen/contrast_dialog_button_total_size"
-            android:background="@drawable/contrast_dialog_button_background">
-
-            <ImageView
-                android:layout_gravity="center"
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                android:src="@drawable/ic_contrast_high"/>
-
-        </FrameLayout>
-
-        <TextView
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing"
-            android:gravity="center_horizontal|top"
-            android:textSize="@dimen/contrast_dialog_button_text_size"
-            android:text="@string/quick_settings_contrast_high"
-            android:textColor="?androidprv:attr/textColorPrimary"/>
-    </LinearLayout>
-
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"/>
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 19fb874..4234fca5 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -21,6 +21,19 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
+    <ImageView
+        android:id="@+id/glanceable_hub_handle"
+        android:layout_width="4dp"
+        android:layout_height="220dp"
+        android:layout_centerVertical="true"
+        android:layout_marginEnd="12dp"
+        android:background="@drawable/hub_handle"
+        android:visibility="gone"
+        android:contentDescription="UI indicator for swiping open the glanceable hub"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/dream_overlay_content"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
similarity index 66%
rename from packages/SystemUI/res/layout/ongoing_call_chip.xml
rename to packages/SystemUI/res/layout/ongoing_activity_chip.xml
index 6a0217ec..a33be12 100644
--- a/packages/SystemUI/res/layout/ongoing_call_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -17,43 +17,45 @@
      the chip. -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/ongoing_call_chip"
+    android:id="@+id/ongoing_activity_chip"
     android:layout_width="wrap_content"
     android:layout_height="match_parent"
     android:layout_gravity="center_vertical|start"
     android:layout_marginStart="5dp"
 >
-    <com.android.systemui.statusbar.phone.ongoingcall.OngoingCallBackgroundContainer
-        android:id="@+id/ongoing_call_chip_background"
+    <!-- TODO(b/332662551): Update this content description when this supports more than just
+         phone calls. -->
+    <com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+        android:id="@+id/ongoing_activity_chip_background"
         android:layout_width="wrap_content"
         android:layout_height="@dimen/ongoing_appops_chip_height"
         android:layout_gravity="center_vertical"
         android:gravity="center"
-        android:background="@drawable/ongoing_call_chip_bg"
-        android:paddingStart="@dimen/ongoing_call_chip_side_padding"
-        android:paddingEnd="@dimen/ongoing_call_chip_side_padding"
+        android:background="@drawable/ongoing_activity_chip_bg"
+        android:paddingStart="@dimen/ongoing_activity_chip_side_padding"
+        android:paddingEnd="@dimen/ongoing_activity_chip_side_padding"
         android:contentDescription="@string/ongoing_phone_call_content_description"
         android:minWidth="@dimen/min_clickable_item_size"
     >
 
         <ImageView
             android:src="@*android:drawable/ic_phone"
-            android:layout_width="@dimen/ongoing_call_chip_icon_size"
-            android:layout_height="@dimen/ongoing_call_chip_icon_size"
+            android:layout_width="@dimen/ongoing_activity_chip_icon_size"
+            android:layout_height="@dimen/ongoing_activity_chip_icon_size"
             android:tint="?android:attr/colorPrimary"
         />
 
-        <com.android.systemui.statusbar.phone.ongoingcall.OngoingCallChronometer
-            android:id="@+id/ongoing_call_chip_time"
+        <com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+            android:id="@+id/ongoing_activity_chip_time"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:singleLine="true"
             android:gravity="center|start"
-            android:paddingStart="@dimen/ongoing_call_chip_icon_text_padding"
+            android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding"
             android:textAppearance="@android:style/TextAppearance.Material.Small"
             android:fontFamily="@*android:string/config_headlineFontFamily"
             android:textColor="?android:attr/colorPrimary"
         />
 
-    </com.android.systemui.statusbar.phone.ongoingcall.OngoingCallBackgroundContainer>
+    </com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer>
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml
index 76f7f3b..49d3a8e 100644
--- a/packages/SystemUI/res/layout/screenshot_shelf.xml
+++ b/packages/SystemUI/res/layout/screenshot_shelf.xml
@@ -33,8 +33,7 @@
             android:layout_width="wrap_content"
             android:elevation="4dp"
             android:background="@drawable/shelf_action_chip_container_background"
-            android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
-            android:layout_marginBottom="@dimen/screenshot_shelf_vertical_margin"
+            android:layout_marginHorizontal="@dimen/overlay_action_container_minimum_edge_spacing"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintBottom_toTopOf="@id/guideline"
             >
@@ -61,7 +60,7 @@
             android:id="@+id/screenshot_preview_border"
             android:layout_width="0dp"
             android:layout_height="0dp"
-            android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
+            android:layout_marginStart="@dimen/overlay_action_container_minimum_edge_spacing"
             android:layout_marginTop="@dimen/overlay_border_width_neg"
             android:layout_marginEnd="@dimen/overlay_border_width_neg"
             android:layout_marginBottom="@dimen/screenshot_shelf_vertical_margin"
@@ -136,11 +135,12 @@
             android:id="@+id/screenshot_scrollable_preview"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:clipToOutline="true"
             android:scaleType="matrix"
             android:visibility="gone"
             app:layout_constraintStart_toStartOf="@id/screenshot_preview"
             app:layout_constraintTop_toTopOf="@id/screenshot_preview"
-            android:elevation="7dp"/>
+            android:elevation="3dp"/>
 
         <androidx.constraintlayout.widget.Guideline
             android:id="@+id/guideline"
@@ -153,7 +153,7 @@
             android:id="@+id/screenshot_message_container"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
-            android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
+            android:layout_marginHorizontal="@dimen/overlay_action_container_minimum_edge_spacing"
             android:layout_marginTop="4dp"
             android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
             android:paddingHorizontal="@dimen/overlay_action_container_padding_end"
@@ -171,6 +171,13 @@
         </FrameLayout>
     </androidx.constraintlayout.widget.ConstraintLayout>
     <ImageView
+        android:id="@+id/screenshot_scrolling_scrim"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"
+        android:clickable="true"
+        android:importantForAccessibility="no"/>
+    <ImageView
         android:id="@+id/screenshot_flash"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 452bc31..4247c7e 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -99,7 +99,7 @@
                         android:gravity="center_vertical|start"
                     />
 
-                    <include layout="@layout/ongoing_call_chip" />
+                    <include layout="@layout/ongoing_activity_chip" />
 
                     <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                         android:id="@+id/notification_icon_area"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index ac523c1..cc8f9c2 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Langdruk om legstukke te pasmaak"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Pasmaak legstukke"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App-ikoon vir gedeaktiveerde legstuk"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Wysig legstuk"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Verwyder"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Voeg legstuk by"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Pasmaak legstukke"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Legstukke op sluitskerm"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"kies legstuk"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"verwyder legstuk"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"plaas gekose legstuk"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aftrekkieslys"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Voer uitsetinstellings in"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Volumeglyers is uitgevou"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Volumeglyers is ingevou"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"demp %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"ontdemp %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> speel tans op"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Oudio sal speel op"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Bel met"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 1236c97..b00dc78 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ምግብሮችን ለማበጀት በረጅሙ ይጫኑ"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ምግብሮችን አብጅ"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"የመተግበሪያ አዶ ለተሰናከለ ምግብር"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"ምግብርን አርትዕ"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"አስወግድ"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ምግብር አክል"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ምግብሮችን አብጅ"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ምግብሮች በማያ ገጽ ቁልፍ ላይ"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ምግብር ይምረጡ"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ምግብር አስወግድ"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"በቦታ የተመረጠ ምግብር"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ወደታች ተጎታች ምናሌ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"የውጽዓት ቅንብሮችን ያስገቡ"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"የድምጽ ተንሸራታቾች ተዘርግቷል"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"የድምጽ ተንሸራታቾች ተሰብስቧል"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s ላይ ድምፀ-ከል አድርግ"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"የ%s ድምፀ-ከል አንሳ"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> እየተጫወተ ያለው በ"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"ኦዲዮ ይጫወታል በ"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"በጥሪ ላይ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 2eed2aa..d498154 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -162,7 +162,7 @@
     <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تمّ التأكيد."</string>
     <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"يمكنك النقر على \"تأكيد\" لإكمال المهمة."</string>
     <string name="biometric_dialog_tap_confirm_with_face" msgid="2378151312221818694">"تم فتح قفل جهازك عند تقريبه من وجهك."</string>
-    <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"تم فتح قفل جهازك عند تقريبه من وجهك. اضغط للمتابعة."</string>
+    <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"تم فتح الجهاز بالتعرّف على وجهك. اضغط للمتابعة."</string>
     <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"تم التعرّف على الوجه. اضغط للمتابعة."</string>
     <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"تم التعرّف على الوجه. للمتابعة، اضغط على رمز فتح القفل."</string>
     <string name="biometric_dialog_authenticated" msgid="7337147327545272484">"مصادقة"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"اضغط مع الاستمرار لتخصيص التطبيقات المصغّرة."</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"تخصيص التطبيقات المصغَّرة"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"رمز التطبيق المصغّر غير المفعّل"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"تعديل التطبيق المصغَّر"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"إزالة"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"إضافة تطبيق مصغّر"</string>
@@ -629,8 +631,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"الدخول إلى إعدادات إخراج الصوت"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"تم توسيع أشرطة تمرير الصوت"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"تم تصغير أشرطة تمرير الصوت"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"‏كتم صوت \"%s\""</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"‏إعادة صوت \"%s\""</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"تشغيل <xliff:g id="LABEL">%s</xliff:g> على"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"سيتم تشغيل الصوت على"</string>
     <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 16cd64b..daefd83 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ৱিজেট কাষ্টমাইজ কৰিবলৈ দীঘলীয়াকৈ টিপক"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ৱিজেট কাষ্টমাইজ কৰক"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"অক্ষম কৰা ৱিজেটৰ বাবে এপৰ চিহ্ন"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"ৱিজেট সম্পাদনা কৰক"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"আঁতৰাওক"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ৱিজেট যোগ দিয়ক"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ৱিজেট কাষ্টমাইজ কৰক"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"লক স্ক্ৰীনত ৱিজেট"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ৱিজেট বাছনি কৰক"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ৱিজেট আঁতৰাওক"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"বাছনি কৰা ৱিজেটটো ৰাখক"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যৱহাৰকাৰী সলনি কৰক"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুল-ডাউনৰ মেনু"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ আটাইবোৰ এপ্ আৰু ডেটা মচা হ\'ব।"</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"আউটপুট ছেটিং খোলক"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"ভলিউমৰ শ্লাইডাৰ বিস্তাৰ কৰা আছে"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"ভলিউমৰ শ্লাইডাৰ সংকোচন কৰা আছে"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s মিউট কৰক"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s আনমিউট কৰক"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"ইয়াত <xliff:g id="LABEL">%s</xliff:g> প্লে’ হৈ আছে"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"অডিঅ’ ইয়াত প্লে’ হ’ব"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"কল চলি আছে"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 69cec1c..399e621 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Basıb saxlayaraq vidcetləri fərdiləşdirin"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Vidcetləri fərdiləşdirin"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Deaktiv edilmiş vidcet üçün tətbiq ikonası"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Vidceti redaktə edin"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Silin"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Vidcet əlavə edin"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Vidcetləri fərdiləşdirin"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Kilid ekranındakı vidcetlər"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vidcet seçin"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"vidceti silin"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"seçilmiş vidceti yerləşdirin"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aşağı çəkilən menyu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Çıxış ayarlarını daxil edin"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Səs slayderləri genişləndirildi"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Səs slayderləri yığcamlaşdırıldı"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s seçimini səssiz edin"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s seçimini səssiz rejimdən çıxarın"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> tətbiqində oxudulur"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio oxudulacaq"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Zəng edilir"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index c2cc34a..16056660 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Dugi pritisak za prilagođavanje vidžeta"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prilagodi vidžete"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ikona aplikacije za onemogućen vidžet"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Izmeni vidžet"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Ukloni"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj vidžet"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Prilagodite vidžete"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Vidžeti na zaključanom ekranu"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"izaberite vidžet"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"uklonite vidžet"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavite izabrani vidžet"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Unesite podešavanja izlaznog signala"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Klizači za jačinu zvuka su prošireni"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Klizači za jačinu zvuka su skupljeni"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"isključite zvuk za: %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"uključite zvuk za: %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> se pušta na"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk se pušta na"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Poziv na uređaju"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Tjuner za korisnički interfejs sistema"</string>
     <string name="status_bar" msgid="4357390266055077437">"Statusna traka"</string>
     <string name="demo_mode" msgid="263484519766901593">"Režim demonstracije za korisnički interfejs sistema"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index f956c0f..89a4701 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -97,8 +97,7 @@
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Левая граніца: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Правая граніца: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
     <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Захавана ў праграму \"<xliff:g id="APP">%1$s</xliff:g>\" (у працоўным профілі)"</string>
-    <!-- no translation found for screenshot_private_profile_notification (1704440899154243171) -->
-    <skip />
+    <string name="screenshot_private_profile_notification" msgid="1704440899154243171">"Захавана ў праграму \"<xliff:g id="APP">%1$s</xliff:g>\" (у прыватным профілі)"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Файлы"</string>
     <string name="screenshot_detected_template" msgid="7940376642921719915">"Праграма \"<xliff:g id="APPNAME">%1$s</xliff:g>\" выявіла гэты здымак экрана."</string>
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> і іншыя адкрытыя праграмы выявілі гэты здымак экрана."</string>
@@ -448,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Доўга націскайце, каб наладзіць віджэты"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Наладзіць віджэты"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Значок праграмы для адключанага віджэта"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Змяніць віджэт"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Выдаліць"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Дадаць віджэт"</string>
@@ -461,12 +462,9 @@
     <string name="accessibility_action_label_close_communal_hub" msgid="6790396569621032333">"Закрыць віджэты на экране блакіроўкі"</string>
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Наладзіць віджэты"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Віджэты на экране блакіроўкі"</string>
-    <!-- no translation found for accessibility_action_label_select_widget (8897281501387398191) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"выбраць віджэт"</string>
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"выдаліць віджэт"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"размясціць выбраны віджэт"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Перайсці да іншага карыстальніка"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"высоўнае меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string>
@@ -631,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Перайсці да налад вываду"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Меню з паўзункамі гучнасці разгорнута"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Меню з паўзункамі гучнасці згорнута"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"выключыць гук (%s)"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"уключыць гук (%s)"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> прайграецца тут:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аўдыявыхад:"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ідзе выклік"</string>
@@ -663,8 +667,7 @@
     <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Спадарожнікавая сувязь, дрэннае падключэнне"</string>
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спадарожнікавая сувязь, добрае падключэнне"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Спадарожнікавая сувязь, падключэнне даступнае"</string>
-    <!-- no translation found for satellite_connected_carrier_text (118524195198532589) -->
-    <skip />
+    <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Экстраннае спадарожнікавае падключэнне"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Працоўны профіль"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Цікава для некаторых, але не для ўсіх"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Наладка сістэмнага інтэрфейсу карыстальніка дае вам дадатковыя спосабы наладжвання і дапасоўвання карыстальніцкага інтэрфейсу Android. Гэтыя эксперыментальныя функцыі могуць змяніцца, перастаць працаваць або знікнуць у будучых версіях. Карыстайцеся з асцярожнасцю."</string>
@@ -869,8 +872,7 @@
     <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Дадаць плітку"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перамясціць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Дадаць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
-    <!-- no translation found for accessibilit_qs_edit_tile_add_move_invalid_position (2858467994472624487) -->
-    <skip />
+    <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Няправільнае месца."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Пазіцыя <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Плітка дададзена"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Плітка выдалена"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index c9d0cd2..bdd9fd4 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Натиснете продължително за персонализ. на приспос."</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Персонализиране на приспособленията"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Икона на приложение за деактивирано приспособление"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Редактиране на приспособлението"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Премахване"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Добавяне на приспособление"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Персонализиране на приспособленията"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Приспособления на заключения екран"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"избиране на приспособление"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"премахване на приспособлението"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"поставяне на избраното приспособление"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Превключване между потребителите"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падащо меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Отваряне на изходните настройки"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Плъзгачите за силата на звука са разгънати"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Плъзгачите за силата на звука са свити"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"спиране на звука на %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"включване на звука на %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Възпроизвеждане на <xliff:g id="LABEL">%s</xliff:g> на"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудиото ще се възпроизвежда на"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Активно обаждане"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index e2b4a6d..2c47b12 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"উইজেট কাস্টমাইজ করতে বেশিক্ষণ প্রেস করুন"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"উইজেট কাস্টমাইজ করুন"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"বন্ধ করা উইজেটের জন্য অ্যাপের আইকন"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"উইজেট এডিট করুন"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"সরান"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"উইজেট যোগ করুন"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"উইজেট কাস্টমাইজ করুন"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"লক স্ক্রিনে উইজেট"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"উইজেট বেছে নিন"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"উইজেট সরান"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"বেছে নেওয়া উইজেটটি রাখুন"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুলডাউন মেনু"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ ও ডেটা মুছে ফেলা হবে।"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"আউটপুট সেটিংস লিখুন"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"ভলিউম স্লাইডার বড় করা হয়েছে"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"ভলিউম স্লাইডার আড়াল করা হয়েছে"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s মিউট করুন"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s আনমিউট করুন"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>-এ প্লে করা হচ্ছে"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"অডিও এতে প্লে করা হবে"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"কল চালু আছে"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"সিস্টেম UI টিউনার"</string>
     <string name="status_bar" msgid="4357390266055077437">"স্ট্যাটাস বার"</string>
     <string name="demo_mode" msgid="263484519766901593">"সিস্টেম UI ডেমো মোড"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 108bed8..8621b43 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -153,7 +153,7 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošalji"</string>
     <string name="cancel" msgid="1089011503403416730">"Otkaži"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logotip aplikacije"</string>
-    <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdite"</string>
+    <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdi"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Pokušaj ponovo"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Dodirnite da otkažete autentifikaciju"</string>
     <string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"Pokušajte ponovo"</string>
@@ -267,7 +267,7 @@
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nema dostupnih uparenih uređaja"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dodirnite da povežete ili prekinete povezanost uređaja"</string>
-    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Upari novi uređaj"</string>
+    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Uparite novi uređaj"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Prikaži sve"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pritisnite i zadržite da prilagodite vidžete"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prilagodite vidžete"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ikona aplikacije za onemogućeni vidžet"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Uredite vidžet"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Uklanjanje"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodajte vidžet"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Prilagođavanje vidžeta"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Vidžeti na zaključanom ekranu"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"odabir vidžeta"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"uklanjanje vidžeta"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavljanje odabranog vidžeta"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Unos postavki izlaza"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Klizači jačine zvuka su prošireni"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Klizači jačine zvuka su suženi"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"isključivanje parametra %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"uključivanje parametra %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Reproducira se <xliff:g id="LABEL">%s</xliff:g> na"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Reprodukcija zvuka na"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Poziv putem"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Podešavač za korisnički interfejs sistema"</string>
     <string name="status_bar" msgid="4357390266055077437">"Statusna traka"</string>
     <string name="demo_mode" msgid="263484519766901593">"Demo način rada Sistemskog UI-ja"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 48fadbc..6ebfd86 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén premut per personalitzar els widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalitza els widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Icona de l\'aplicació per a widget desactivat"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Edita el widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Suprimeix"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Afegeix un widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Personalitza els widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets a la pantalla de bloqueig"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"selecciona el widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"suprimeix el widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"col·loca el widget seleccionat"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Introdueix opcions de configuració de sortida"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Els controls lliscants de volum s\'han desplegat"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Els controls lliscants de volum s\'han replegat"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"silencia %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"deixa de silenciar %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"S\'està reproduint <xliff:g id="LABEL">%s</xliff:g> a"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Es reproduirà a"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Trucant des de"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Personalitzador d\'interfície d\'usuari"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barra d\'estat"</string>
     <string name="demo_mode" msgid="263484519766901593">"Mode de demostració de la IU del sistema"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index c90a023..7d2202d 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -269,15 +269,15 @@
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Klepnutím zařízení připojíte nebo odpojíte"</string>
     <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Spárovat nové zařízení"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Zobrazit vše"</string>
-    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Použít Bluetooth"</string>
+    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Používat Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Připojeno"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Sdílení zvuku"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uloženo"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojit"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovat"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Zítra znovu automaticky zapnout"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkce jako Quick Share a Najdi moje zařízení využívají Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se zapne zítra ráno"</string>
+    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth využívají funkce jako Quick Share a Najdi moje zařízení."</string>
+    <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se zapne zítra ráno."</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Sdílení zvuku"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sdílení zvuku"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Dlouhým stisknutím můžete přizpůsobit widgety"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Přizpůsobit widgety"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ikona aplikace s deaktivovaným widgetem"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Upravit widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Odstranit"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Přidat widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Přizpůsobit widgety"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgety na obrazovce uzamčení"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vybrat widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"odstranit widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"umístit vybraný widget"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbalovací nabídka"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Otevřít nastavení výstupu"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Posuvníky hlasitosti jsou rozbalené"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Posuvníky hlasitosti jsou sbalené"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"ztlumíte %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"zapnete zvuk %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Přehrávání <xliff:g id="LABEL">%s</xliff:g> přes"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk se bude přehrávat přes"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Volání na zařízení"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Nástroj na ladění uživatelského rozhraní systému"</string>
     <string name="status_bar" msgid="4357390266055077437">"Stavový řádek"</string>
     <string name="demo_mode" msgid="263484519766901593">"Ukázkový režim uživatelského rozhraní systému"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 036f855..ec03938 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Hold fingeren nede for at tilpasse widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Tilpas widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Appikon for deaktiveret widget"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Rediger widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Fjern"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tilføj widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Tilpas widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets på låseskærmen"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vælg widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"fjern widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"placer valgt widget"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullemenu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Angiv indstillinger for output"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Lydstyrkeskydere er udvidet"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Lydstyrkeskydere er skjult"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"slå lyden fra for %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"slå lyden til for %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Afspiller <xliff:g id="LABEL">%s</xliff:g> på"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Lyden afspilles på"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ringer på"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 42efdc2..d38ad3a 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Lange drücken, um Widgets anzupassen"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Widgets anpassen"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App-Symbol für deaktiviertes Widget"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Widget bearbeiten"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Entfernen"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget hinzufügen"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Widgets anpassen"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets auf dem Sperrbildschirm"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"Widget auswählen"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"Widget entfernen"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ausgewähltes Widget in den Bearbeitungsmodus versetzen"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Nutzer wechseln"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Pull-down-Menü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Ausgabeeinstellungen angeben"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Lautstärkeregler maximiert"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Lautstärkeregler minimiert"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s stummzuschalten"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"Stummschaltung von %s aufzuheben"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Wiedergabe von <xliff:g id="LABEL">%s</xliff:g> über"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audiowiedergabe über"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Anruf auf"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Statusleiste"</string>
     <string name="demo_mode" msgid="263484519766901593">"Demomodus der System-UI"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 72ab884..c6dad44 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Παρατεταμένο πάτημα για προσαρμογή γραφ. στοιχείων"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Προσαρμογή γραφικών στοιχείων"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Εικονίδιο εφαρμογής για απενεργοποιημένο γραφικό στοιχείο"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Επεξεργασία γραφικού στοιχείου"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Κατάργηση"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Προσθήκη γραφικού στοιχείου"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Προσαρμογή γραφικών στοιχείων"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Γραφικά στοιχεία στην οθόνη κλειδώματος"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"επιλογή γραφικού στοιχείου"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"κατάργηση γραφικού στοιχείου"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"τοποθέτηση επιλεγμένου γραφικού στοιχείου"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"αναπτυσσόμενο μενού"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Εισαγωγή ρυθμίσεων εξόδου"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Τα ρυθμιστικά έντασης ήχου αναπτύχθηκαν"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Τα ρυθμιστικά έντασης ήχου συμπτύχθηκαν"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"σίγαση %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"κατάργηση σίγασης %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Αναπαραγωγή <xliff:g id="LABEL">%s</xliff:g> σε"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Ο ήχος θα παίξει σε"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ενεργή κλήση σε"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Γραμμή κατάστασης"</string>
     <string name="demo_mode" msgid="263484519766901593">"Λειτουργία επίδειξης διεπαφής χρήστη συστήματος"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index e414cd4..cbb2298 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Customise widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App icon for disabled widget"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Customise widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets on lock screen"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"select widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remove widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Enter output settings"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Volume sliders expanded"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Volume sliders collapsed"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"mute %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"unmute %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Calling on"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
     <string name="demo_mode" msgid="263484519766901593">"System UI demo mode"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 204f21d..fa007b6 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -447,6 +447,7 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customize widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Customize widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App icon for disabled widget"</string>
+    <string name="icon_description_for_pending_widget" msgid="8413816401868001755">"App icon for a widget being installed"</string>
     <string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string>
@@ -627,8 +628,10 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Enter output settings"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Volume sliders expanded"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Volume sliders collapsed"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"mute %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"unmute %s"</string>
+    <string name="volume_panel_hint_mute" msgid="2153922288568199077">"Mute %s"</string>
+    <string name="volume_panel_hint_unmute" msgid="4831850937582282340">"Unmute %s"</string>
+    <string name="volume_panel_hint_muted" msgid="1124844870181285320">"muted"</string>
+    <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibrate"</string>
     <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Calling on"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index e414cd4..cbb2298 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Customise widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App icon for disabled widget"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Customise widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets on lock screen"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"select widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remove widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Enter output settings"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Volume sliders expanded"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Volume sliders collapsed"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"mute %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"unmute %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Calling on"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
     <string name="demo_mode" msgid="263484519766901593">"System UI demo mode"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index e414cd4..cbb2298 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Customise widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App icon for disabled widget"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Customise widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets on lock screen"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"select widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remove widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Enter output settings"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Volume sliders expanded"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Volume sliders collapsed"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"mute %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"unmute %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Calling on"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
     <string name="demo_mode" msgid="263484519766901593">"System UI demo mode"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index ee28a62..248360b 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -447,6 +447,7 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎Long press to customize widgets‎‏‎‎‏‎"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎Customize widgets‎‏‎‎‏‎"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‎App icon for disabled widget‎‏‎‎‏‎"</string>
+    <string name="icon_description_for_pending_widget" msgid="8413816401868001755">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‎App icon for a widget being installed‎‏‎‎‏‎"</string>
     <string name="edit_widget" msgid="9030848101135393954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‎‏‎‎Edit widget‎‏‎‎‏‎"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎Remove‎‏‎‎‏‎"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‎Add widget‎‏‎‎‏‎"</string>
@@ -627,8 +628,10 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‏‎Enter output settings‎‏‎‎‏‎"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎Volume sliders expanded‎‏‎‎‏‎"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‏‏‎‏‎‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎Volume sliders collapsed‎‏‎‎‏‎"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎‏‎‎mute %s‎‏‎‎‏‎"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‎‎‏‏‎‎unmute %s‎‏‎‎‏‎"</string>
+    <string name="volume_panel_hint_mute" msgid="2153922288568199077">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‎‏‎Mute %s‎‏‎‎‏‎"</string>
+    <string name="volume_panel_hint_unmute" msgid="4831850937582282340">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‏‎‎‏‎‎‎Unmute %s‎‏‎‎‏‎"</string>
+    <string name="volume_panel_hint_muted" msgid="1124844870181285320">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎muted‎‏‎‎‏‎"</string>
+    <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎‎‎vibrate‎‏‎‎‏‎"</string>
     <string name="media_output_label_title" msgid="872824698593182505">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎Playing ‎‏‎‎‏‏‎<xliff:g id="LABEL">%s</xliff:g>‎‏‎‎‏‏‏‎ on‎‏‎‎‏‎"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎Audio will play on‎‏‎‎‏‎"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎Calling on‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 42424ef..28230f1 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén presionado para personalizar los widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ícono de la app de widget inhabilitado"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Modificar widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Agregar widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Personalizar widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets en la pantalla de bloqueo"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"Seleccionar widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"quitar widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"colocar widget seleccionado"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú expandible"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string>
@@ -619,7 +619,7 @@
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Audio espacial"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Desactivar"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fijar"</string>
-    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seg. de cabeza"</string>
+    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Monitoreo de cabeza"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Presiona para cambiar el modo de timbre"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Ingresar configuración de salida"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Controles deslizantes del volumen expandidos"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Controles deslizantes del volumen colapsados"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"silenciar %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"activar sonido %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Reproduciendo <xliff:g id="LABEL">%s</xliff:g> en"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Se reproducirá en"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Llamando en"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador de IU del sistema"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barra de estado"</string>
     <string name="demo_mode" msgid="263484519766901593">"Modo demostración de la IU del sistema"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 77989e4..fd2ac85 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén pulsado para personalizar los widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Icono de la aplicación de widget inhabilitado"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Editar widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Añadir widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Personalizar widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets en la pantalla de bloqueo"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"seleccionar widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"eliminar widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"colocar widget seleccionado"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Introducir los ajustes de salida"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Controles deslizantes de volumen desplegados"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Controles deslizantes de volumen contraídos"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"silenciar %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"dejar de silenciar %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Reproduciendo <xliff:g id="LABEL">%s</xliff:g> en"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Se reproducirá en"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Llamando desde"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Configurador de UI del sistema"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barra de estado"</string>
     <string name="demo_mode" msgid="263484519766901593">"Modo Demo de UI del sistema"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index a3663a0..7f97176 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Vajutage pikalt vidinate kohandamiseks"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Kohanda vidinaid"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Keelatud vidina rakenduseikoon"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Muuda vidinat"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Eemalda"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lisa vidin"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Kohanda vidinaid"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Lukustuskuva vidinad"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vidina valimine"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"eemaldage vidin"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"asetage valitud vidin"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kasutaja vahetamine"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rippmenüü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Sisestage väljundseaded"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Helitugevuse liugurid laiendatud"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Helitugevuse liugurid ahendatud"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"vaigistab %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"tühistab %s vaigistuse"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Esitamine jätkub seadmes <xliff:g id="LABEL">%s</xliff:g>"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Heli esitatakse seadmes"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Helistamine seadmes"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Süsteemi kasutajaliidese tuuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Olekuriba"</string>
     <string name="demo_mode" msgid="263484519766901593">"Süsteemi kasutajaliidese demorežiim"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 23de72e..685b592 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Widgetak pertsonalizatzeko, sakatu luze"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Pertsonalizatu widgetak"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Desgaitutako widgetaren aplikazio-ikonoa"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Editatu widgeta"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Kendu"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Gehitu widget bat"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Pertsonalizatu widgetak"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Pantaila blokeatuko widgetak"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"hautatu widget bat"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"kendu widgeta"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"kokatu hautatutako widgeta"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"zabaldu menua"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Ireki emaitzaren ezarpenak"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Bolumenaren botoi lerrakorrak zabalduta daude"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Bolumenaren botoi lerrakorrak tolestuta daude"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"desaktibatu honen audioa: %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"aktibatu honen audioa: %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> hemen erreproduzitzen:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audioak abian jarraituko du hemen:"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Honen bidez deitzen"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Sistemaren erabiltzaile-interfazearen konfiguratzailea"</string>
     <string name="status_bar" msgid="4357390266055077437">"Egoera-barra"</string>
     <string name="demo_mode" msgid="263484519766901593">"Sistemaren erabiltzaile-interfazearen demo modua"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 9feeaee..d21fdd3 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"برای سفارشی‌سازی ابزارک‌ها، فشار طولانی دهید"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"سفارشی‌سازی ابزارک‌ها"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"نماد برنامه برای ابزارک غیرفعال"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"ویرایش ابزارک"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"برداشتن"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"افزودن ابزارک"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"سفارشی‌سازی ابزارک‌ها"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ابزارک‌ها در صفحه قفل"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"انتخاب ابزارک"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"برداشتن ابزارک"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"جای‌گذاری ابزارک انتخاب‌شده"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"منوی پایین‌پر"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامه‌ها و داده‌های این جلسه حذف خواهد شد."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"تنظیمات خروجی را وارد کنید"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"لغزنده‌های صدا ازهم باز شدند"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"لغزنده‌های صدا جمع شدند"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"‏بی‌صدا کردن %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"‏باصدا کردن %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"درحال پخش <xliff:g id="LABEL">%s</xliff:g> در"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"صدا در این دستگاه پخش می‌شود:"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"تماس برقرار است"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"تنظیم‌کننده واسط کاربری سیستم"</string>
     <string name="status_bar" msgid="4357390266055077437">"نوار وضعیت"</string>
     <string name="demo_mode" msgid="263484519766901593">"حالت نمایشی واسط کاربری سیستم"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 68319fd..8910bf3 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Yksilöi widgetit pitkällä painalluksella"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Muokkaa widgettejä"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Käytöstä poistetun widgetin sovelluskuvake"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Muokkaa widgetiä"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Poista"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lisää widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Muokkaa widgetejä"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgetit lukitusnäytöllä"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"valitse widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"poista widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"aseta valittu widget"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Vaihda käyttäjää"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"alasvetovalikko"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Lisää tuloasetukset"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Äänenvoimakkuuden liukusäätimet laajennettu"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Äänenvoimakkuuden liukusäätimet tiivistetty"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"mykistä: %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"poista mykistys: %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Toistetaan: <xliff:g id="LABEL">%s</xliff:g>"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audiota toistetaan laitteella"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Puhelu kesken:"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Tilapalkki"</string>
     <string name="demo_mode" msgid="263484519766901593">"Käyttöliittymän esittelytila"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index fd6ec7a..df5f59f 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Maintenez le doigt pour personnaliser les widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personnaliser les widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Icône d\'application pour un widget désactivé"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Modifier le widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Retirer"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ajouter un widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Personnaliser les widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets sur l\'écran de verrouillage"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"sélectionner le widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"retirer le widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"placer le widget sélectionné"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Entrer les paramètres de sortie"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Curseurs de volume développés"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Curseurs de volume réduits"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"Désactivez le son de %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"Réactivez le son de %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Lecture de <xliff:g id="LABEL">%s</xliff:g> sur"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Lecture audio sur"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Appel en cours"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barre d\'état"</string>
     <string name="demo_mode" msgid="263484519766901593">"Mode Démo de l\'interface système"</string>
@@ -1012,10 +1017,10 @@
     <string name="accessibility_floating_button_hidden_notification_text" msgid="1457021647040915658">"Touchez pour afficher le bouton d\'accessibilité"</string>
     <string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"Le raccourci <xliff:g id="FEATURE_NAME">%s</xliff:g> a été retiré"</string>
     <string name="accessibility_floating_button_undo_message_number_text" msgid="4909270290725226075">"{count,plural, =1{# raccourci retiré}one{# raccourci retiré}many{# de raccourcis retirés}other{# raccourcis retirés}}"</string>
-    <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer dans coin sup. gauche"</string>
-    <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer dans coin sup. droit"</string>
-    <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Déplacer dans coin inf. gauche"</string>
-    <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Déplacer dans coin inf. droit"</string>
+    <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer en haut à gauche"</string>
+    <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer en haut à droite"</string>
+    <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Déplacer en bas à gauche"</string>
+    <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Déplacer en bas à droite"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Rapprocher du bord et masquer"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Éloigner du bord et afficher"</string>
     <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Retirer"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 47f469f..b02d547 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Appuyez de manière prolongée pour personnaliser les widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personnaliser les widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Icône d\'appli du widget désactivé"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Modifier le widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Supprimer"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ajouter un widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Personnaliser les widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets sur l\'écran de verrouillage"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"sélectionner un widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"supprimer le widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"positionner le widget sélectionné"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Accéder aux paramètres de sortie"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Curseurs de volume développés"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Curseurs de volume réduits"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"couper le son de %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"réactiver le son de %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Diffusion de <xliff:g id="LABEL">%s</xliff:g> sur"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Lecture audio sur"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Appel défini sur"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index e7037b3..8e2a011 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pulsación longa para personalizar os widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Icona da aplicación de widget desactivado"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Editar widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Engadir widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Personalizar os widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets na pantalla de bloqueo"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"seleccionar widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"quitar o widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"colocar o widget seleccionado"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú despregable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Introducir a configuración de saída"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Controis desprazables de volume despregados"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Controis desprazables de volume contraídos"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"silenciar %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"activar o son de %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Reproducindo <xliff:g id="LABEL">%s</xliff:g> en"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Reproducirase en"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Chamada en curso"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Configurador da IU do sistema"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barra de estado"</string>
     <string name="demo_mode" msgid="263484519766901593">"Modo de demostración da IU do sistema"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index b3c8e1eb..c0f6be9 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"વિજેટ કસ્ટમાઇઝ કરવા માટે થોડીવાર દબાવી રાખો"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"વિજેટ કસ્ટમાઇઝ કરો"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"બંધ કરેલા વિજેટ માટેની ઍપનું આઇકન"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"વિજેટમાં ફેરફાર કરો"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"કાઢી નાખો"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"વિજેટ ઉમેરો"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"વિજેટ કસ્ટમાઇઝ કરો"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"લૉક સ્ક્રીન પર વિજેટ"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"વિજેટ પસંદ કરો"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"વિજેટ કાઢી નાખો"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"પસંદ કરેલું વિજેટ મૂકો"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"વપરાશકર્તા સ્વિચ કરો"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"પુલડાઉન મેનૂ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ અને ડેટા કાઢી નાખવામાં આવશે."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"આઉટપુટના સેટિંગ દાખલ કરો"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"વૉલ્યૂમના સ્લાઇડર મોટા કરવામાં આવ્યા"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"વૉલ્યૂમના સ્લાઇડર નાના કરવામાં આવ્યા"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%sને મ્યૂટ કરો"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%sને અનમ્યૂટ કરો"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> વગાડી રહ્યાં છીએ"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"ઑડિયો આની પર વાગશે"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"કૉલ ચાલુ છે"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 2882584..4cf74b5 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"विजेट पसंद के मुताबिक बनाने के लिए उसे दबाकर रखें"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"विजेट अपनी पसंद के मुताबिक बनाएं"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"बंद किए गए विजेट के लिए ऐप्लिकेशन आइकॉन"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"विजेट में बदलाव करें"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"हटाएं"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"विजेट जोड़ें"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"विजेट अपनी पसंद के मुताबिक बनाएं"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"लॉक स्क्रीन पर विजेट"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"विजेट चुनें"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"विजेट हटाएं"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"चुने गए विजेट के लिए जगह चुनें"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेन्यू"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सेशन के सभी ऐप्लिकेशन और डेटा को हटा दिया जाएगा."</string>
@@ -562,7 +562,7 @@
     <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"इस डिवाइस का प्रबंधन आपके अभिभावक करते हैं. अभिभावक आपके डिवाइस से जुड़ी जानकारी देख सकते हैं. साथ ही, इसे प्रबंधित कर सकते हैं. इनमें आपके इस्तेमाल किए गए ऐप्लिकेशन, जगह की जानकारी, और डिवाइस के इस्तेमाल में बिताए गए समय जैसी जानकारी शामिल है."</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"वीपीएन"</string>
     <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent की वजह से अनलॉक रखा गया है"</string>
-    <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"कई बार पुष्टि करने की कोशिश की वजह से, डिवाइस लॉक है"</string>
+    <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"कई बार पुष्टि करने की कोशिशों की वजह से, डिवाइस लॉक हो गया है"</string>
     <string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"डिवाइस लॉक हो गया है\nपुष्टि नहीं की जा सकी"</string>
     <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="accessibility_volume_settings" msgid="1458961116951564784">"साउंड सेटिंग"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"आउटपुट की सेटिंग डालें"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"आवाज़ के स्लाइडर को बड़ा किया गया"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"आवाज़ के स्लाइडर को छोटा किया गया"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s को म्यूट करें"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s को अनम्यूट करें"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> इस पर चल रहा है"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"ऑडियो इस पर चलेगा"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"कॉल चालू है"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"सिस्टम यूज़र इंटरफ़ेस (यूआई) ट्यूनर"</string>
     <string name="status_bar" msgid="4357390266055077437">"स्टेटस बार"</string>
     <string name="demo_mode" msgid="263484519766901593">"सिस्टम यूज़र इंटरफ़ेस (यूआई) डेमो मोड"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 560938b..b211c94 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -369,8 +369,8 @@
     <string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Srednji"</string>
     <string name="quick_settings_contrast_high" msgid="656049259587494499">"Visoki"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušna pomagala"</string>
-    <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni uređaji"</string>
-    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparivanje novog uređaja"</string>
+    <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušna pomagala"</string>
+    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili novi uređaj"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje unaprijed definiranih postavki nije uspjelo"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite li deblokirati mikrofon uređaja?"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Dugo pritisnite za prilagodbu widgeta"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prilagodi widgete"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ikona aplikacije za onemogućeni widget"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Uredi widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Ukloni"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Prilagodi widgete"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgeti na zaključanom zaslonu"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"odaberi widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ukloni widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavi odabrani widget"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući izbornik"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Izbrisat će se sve aplikacije i podaci u ovoj sesiji."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Unesite postavke izlaza"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Proširivanje klizača za glasnoću"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Sažimanje klizača za glasnoću"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"isključili zvuk za sljedeće: %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"uključili zvuk za sljedeće: %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> se reproducira na"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk će se reproducirati na"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Pozivanje na uređaju"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Ugađanje korisničkog sučelja sustava"</string>
     <string name="status_bar" msgid="4357390266055077437">"Traka statusa"</string>
     <string name="demo_mode" msgid="263484519766901593">"Demo način korisničkog sučelja sustava"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 2316643..e736537 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Nyomja meg hosszan a modulok személyre szabásához"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Modulok személyre szabása"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Letiltott modul alkalmazásikonja"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Modul szerkesztése"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Eltávolítás"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Modul hozzáadása"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Modulok személyre szabása"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Modulok a lezárási képernyőn"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"modul kiválasztása"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"modul törlése"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"kijelölt modul áthelyezése"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"lehúzható menü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Kimenet beállításainak megadása"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Hangerő-szabályozók kibontva"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Hangerő-szabályozók összecsukva"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s némítása"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s némításának feloldása"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> lejátszása itt:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Hang lejátszása itt:"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Hívás folyamatban itt:"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Kezelőfelület-hangoló"</string>
     <string name="status_bar" msgid="4357390266055077437">"Állapotsor"</string>
     <string name="demo_mode" msgid="263484519766901593">"A rendszer kezelőfelületének demómódja"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 3d70b70..6a905c5 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Երկար սեղմեք՝ վիջեթները հարմարեցնելու համար"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Հարմարեցնել վիջեթները"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Հավելվածի պատկերակ անջատված վիջեթի համար"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Փոփոխել վիջեթը"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Հեռացնել"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ավելացնել վիջեթ"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Հարմարեցնել վիջեթները"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Վիջեթներ կողպէկրանին"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ընտրել վիջեթ"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"հեռացնել վիջեթը"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"տեղադրել ընտրված վիջեթը"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Անջատել օգտվողին"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"իջնող ընտրացանկ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն:"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Բացել նվագարկման կարգավորումները"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Ձայնի ուժգնության սահիչները ծավալված են"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Ձայնի ուժգնության սահիչները ծալված են"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"անջատել ձայնը (%s)"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"միացնել ձայնը (%s)"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>. նվագարկվում է"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Աուդիոն կնվագարկի"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Զանգն ընթացքում է"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Համակարգի ՕՄ-ի կարգավորիչ"</string>
     <string name="status_bar" msgid="4357390266055077437">"Կարգավիճակի գոտի"</string>
     <string name="demo_mode" msgid="263484519766901593">"Համակարգի միջերեսի ցուցադրական ռեժիմ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index bc7bb0f..23d8397 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Tekan lama untuk menyesuaikan widget"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Sesuaikan widget"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ikon aplikasi untuk widget yang dinonaktifkan"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Hapus"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tambahkan widget"</string>
@@ -629,12 +631,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Masukkan setelan perangkat output"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Penggeser volume diluaskan"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Penggeser volume diciutkan"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"membisukan %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"membunyikan %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Memutar <xliff:g id="LABEL">%s</xliff:g> di"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio akan diputar di"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Menelepon di"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Penyetel Antarmuka Pengguna Sistem"</string>
     <string name="status_bar" msgid="4357390266055077437">"Bilah status"</string>
     <string name="demo_mode" msgid="263484519766901593">"Mode demo UI sistem"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index b30c898..2c77eb1 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Haltu inni til að sérsníða græjur"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Sérsníða græjur"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Forritstákn fyrir græju sem slökkt er á"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Breyta græju"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Fjarlægja"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Bæta græju við"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Sérsníða græjur"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Græjur á lásskjá"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"velja græju"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"fjarlægja græju"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"koma valinni græju fyrir"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Fellivalmynd"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Færa inn stillingar úttaks"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Stækkaðir hljóðstyrkssleðar"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Minnkaðir hljóðstyrkssleðar"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"þagga %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"kveikja á hljóði %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Í spilun í <xliff:g id="LABEL">%s</xliff:g>"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Hljóð heldur áfram að spilast"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Símtal í gangi"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Fínstillingar kerfisviðmóts"</string>
     <string name="status_bar" msgid="4357390266055077437">"Stöðustika"</string>
     <string name="demo_mode" msgid="263484519766901593">"Prufustilling kerfisviðmóts"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 2102099..056c286 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Premi a lungo per personalizzare i widget"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizza widget"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Icona dell\'app per widget disattivati"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Modifica widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Rimuovi"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Aggiungi widget"</string>
@@ -629,12 +631,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Inserisci impostazioni di uscita"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Cursori volume espansi"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Cursori volume compressi"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"disattivare audio di %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"riattivare audio di %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> in riproduzione su"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio riprodotto su:"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Chiamata in corso"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Ottimizzatore UI di sistema"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barra di stato"</string>
     <string name="demo_mode" msgid="263484519766901593">"Modalità demo dell\'interfaccia utente di sistema"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 5be4172..6670b09 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -269,7 +269,7 @@
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"אפשר להקיש כדי להתחבר למכשיר או להתנתק ממנו"</string>
     <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"התאמה של מכשיר חדש"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"הצגת הכול"</string>
-    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth"</string>
+    <string name="turn_on_bluetooth" msgid="5681370462180289071">"‏שימוש ב-Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"מחובר"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"שיתוף אודיו"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"לוחצים לחיצה ארוכה כדי להתאים אישית את הווידג\'טים"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"התאמה אישית של ווידג\'טים"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"סמל האפליקציה לווידג\'ט שהושבת"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"עריכת הווידג\'ט"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"הסרה"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"הוספת ווידג\'ט"</string>
@@ -629,12 +631,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"הזנה של הגדרות הפלט"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"פסי ההזזה של עוצמת הקול במצב מורחב"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"פסי ההזזה של עוצמת הקול במצב מכווץ"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"‏השתקה של %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"‏ביטול ההשתקה של %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"הפעלה של <xliff:g id="LABEL">%s</xliff:g> במכשיר"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"האודיו יופעל במכשיר"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"מתבצעת שיחה במכשיר"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"שורת סטטוס"</string>
     <string name="demo_mode" msgid="263484519766901593">"מצב הדגמה בממשק המשתמש של המערכת"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 62a3a43..e3c0dbc 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -272,7 +272,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth を使用"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"接続しました"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"音声の共有"</string>
-    <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"保存しました"</string>
+    <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"保存済み"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"接続を解除"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"有効化"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明日自動的に ON に戻す"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長押ししてウィジェットをカスタマイズ"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ウィジェットのカスタマイズ"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"無効なウィジェットのアプリアイコン"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"ウィジェットを編集"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"削除"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ウィジェットを追加"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ウィジェットのカスタマイズ"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ロック画面のウィジェット"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ウィジェットを選択"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ウィジェットを削除"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"選択したウィジェットを配置"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"プルダウン メニュー"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"出力の設定を入力してください"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"音量スライダーを開きました"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"音量スライダーを閉じました"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s をミュート"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s のミュートを解除"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> の再生先:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"音声の再生先"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"通話中"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"システムUI調整ツール"</string>
     <string name="status_bar" msgid="4357390266055077437">"ステータスバー"</string>
     <string name="demo_mode" msgid="263484519766901593">"システム UI デモモード"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 2a4f5d6..0251ac0 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ხანგრძლივად დააჭირეთ ვიჯეტების მოსარგებად"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ვიჯეტების მორგება"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"აპის ხატულა გათიშული ვიჯეტისთვის"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"ვიჯეტის რედაქტირება"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"ამოშლა"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ვიჯეტის დამატება"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ვიჯეტების მორგება"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ვიჯეტები ჩაკეტილ ეკრანზე"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ვიჯეტის არჩევა"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ვიჯეტის ამოშლა"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"არჩეული ვიჯეტის განთავსება"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"მომხმარებლის გადართვა"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ჩამოშლადი მენიუ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"აუდიოს გამოსვლის პარამეტრების გახსნა"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"ხმის სლაიდერების გაფართოება"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"ხმის სლაიდერების ჩაკეცვა"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s-ის დადუმება"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s-ის დადუმების მოხსნა"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"უკრავს <xliff:g id="LABEL">%s</xliff:g>:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"აუდიო დაიკვრება"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"მიმდინარეობს ზარი"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 398677e..9c96a4e 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Виджеттерді бейімдеу үшін ұзақ басып тұрыңыз."</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Виджеттерді реттеу"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Өшірілген виджеттің қолданба белгішесі"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Виджетті өзгерту"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Өшіру"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Виджет қосу"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Виджеттерді бейімдеу"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Құлыптаулы экрандағы виджеттер"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"виджет таңдау"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"виджетті өшіру"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"таңдалған виджетті орналастыру"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Пайдаланушыны ауыстыру"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ашылмалы мәзір"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданба мен дерек жойылады."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Шығыс параметрлерін енгізу"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Дыбыс деңгейінің жүгірткі реттегіштері жайылды."</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Дыбыс деңгейінің жүгірткі реттегіштері жиылды."</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s дыбысын өшіру"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s дыбысын қосу"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ойнатылатын құрылғы:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудио ойнатылатын құрылғы:"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Қоңырау шалып жатыр"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Жүйелік пайдаланушылық интерфейс тюнері"</string>
     <string name="status_bar" msgid="4357390266055077437">"Күйін көрсету жолағы"</string>
     <string name="demo_mode" msgid="263484519766901593">"Жүйе интерфейсінің демо режимі"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index e32da00..dc9a747 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ចុច​ឱ្យ​យូរ ដើម្បីប្ដូរធាតុ​ក្រាហ្វិកតាមបំណង"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ប្ដូរ​ធាតុ​ក្រាហ្វិកតាម​បំណង"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"រូបកម្មវិធីសម្រាប់ធាតុ​ក្រាហ្វិកដែលបានបិទ"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"កែធាតុ​ក្រាហ្វិក"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"ដកចេញ"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"បញ្ចូលធាតុ​ក្រាហ្វិក"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ប្ដូរ​ធាតុ​ក្រាហ្វិកតាម​បំណង"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ធាតុ​ក្រាហ្វិកនៅលើអេក្រង់ចាក់សោ"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ជ្រើសរើសធាតុ​ក្រាហ្វិក"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ដកធាតុ​ក្រាហ្វិកចេញ"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ដាក់ធាតុ​ក្រាហ្វិកដែលបានជ្រើសរើស"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរ​អ្នក​ប្រើ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ម៉ឺនុយ​ទាញចុះ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យ​ទាំងអស់​ក្នុង​វគ្គ​នេះ​នឹង​ត្រូវ​លុប។"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"ចូលការកំណត់ឧបករណ៍មេឌៀ"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"បានពង្រីកគ្រាប់រំកិលកម្រិតសំឡេង"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"បានបង្រួមគ្រាប់រំកិលកម្រិតសំឡេង"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"បិទសំឡេង %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"បើក​សំឡេង %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"កំពុងចាក់​​ <xliff:g id="LABEL">%s</xliff:g> នៅ​លើ"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"សំឡេងនឹងលេងនៅលើ"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"កំពុងនិយាយទូរសព្ទ"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"កម្មវិធីសម្រួល UI ប្រព័ន្ធ"</string>
     <string name="status_bar" msgid="4357390266055077437">"របារស្ថានភាព"</string>
     <string name="demo_mode" msgid="263484519766901593">"មុខងារ​សាកល្បង​ UI ប្រព័ន្ធ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index e217f8e..46a679c 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -368,7 +368,7 @@
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ಪ್ರಮಾಣಿತ"</string>
     <string name="quick_settings_contrast_medium" msgid="5158352575583902566">"ಮಧ್ಯಮ"</string>
     <string name="quick_settings_contrast_high" msgid="656049259587494499">"ಹೆಚ್ಚು"</string>
-    <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ಹಿಯರಿಂಗ್ ಸಾಧನಗಳು"</string>
+    <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ಹಿಯರಿಂಗ್ ಸಾಧನಗಳು"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ಹೊಸ ಸಾಧನವನ್ನು ಪೇರ್ ಮಾಡಿ"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ವಿಜೆಟ್‌ಗಳನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ದೀರ್ಘಕಾಲ ಒತ್ತಿರಿ"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ವಿಜೆಟ್‌ಗಳನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾದ ವಿಜೆಟ್‌ಗಾಗಿ ಆ್ಯಪ್ ಐಕಾನ್"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"ವಿಜೆಟ್ ಅನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"ತೆಗೆದುಹಾಕಿ"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ವಿಜೆಟ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ವಿಜೆಟ್‌ಗಳನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ವಿಜೆಟ್ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ವಿಜೆಟ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ಆಯ್ಕೆಮಾಡಿದ ವಿಜೆಟ್ ಅನ್ನು ಇರಿಸಿ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ಪುಲ್‌ಡೌನ್ ಮೆನು"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಶನ್‌ನಲ್ಲಿನ ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
@@ -594,9 +594,9 @@
     <string name="screen_pinning_negative" msgid="6882816864569211666">"ಬೇಡ"</string>
     <string name="screen_pinning_start" msgid="7483998671383371313">"ಆ್ಯಪ್ ಪಿನ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="screen_pinning_exit" msgid="4553787518387346893">"ಆ್ಯಪ್ ಅನ್‌ಪಿನ್ ಮಾಡಲಾಗಿದೆ"</string>
-    <string name="stream_voice_call" msgid="7468348170702375660">"ಕರೆಮಾಡಿ"</string>
+    <string name="stream_voice_call" msgid="7468348170702375660">"ಕರೆ ಮಾಡಿ"</string>
     <string name="stream_system" msgid="7663148785370565134">"ಸಿಸ್ಟಂ"</string>
-    <string name="stream_ring" msgid="7550670036738697526">"ರಿಂಗ್"</string>
+    <string name="stream_ring" msgid="7550670036738697526">"ರಿಂಗ್ ಮಾಡಿ"</string>
     <string name="stream_music" msgid="2188224742361847580">"ಮಾಧ್ಯಮ"</string>
     <string name="stream_alarm" msgid="16058075093011694">"ಅಲಾರಮ್"</string>
     <string name="stream_notification" msgid="7930294049046243939">"ನೋಟಿಫಿಕೇಶನ್"</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"ಔಟ್‌ಪುಟ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"ವಾಲ್ಯೂಮ್ ಸ್ಲೈಡರ್‌ಗಳನ್ನು ವಿಸ್ತೃತಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"ವಾಲ್ಯೂಮ್ ಸ್ಲೈಡರ್‌ಗಳನ್ನು ಕುಗ್ಗಿಸಲಾಗಿದೆ"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಆಗು..."</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"ಇದರಲ್ಲಿ ಪ್ಲೇ ಆಗುತ್ತದೆ"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"ಕರೆ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 3ef3dc8..54dcfce 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"위젯을 맞춤설정하려면 길게 누르기"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"위젯 맞춤설정"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"사용 중지된 위젯의 앱 아이콘"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"위젯 수정"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"삭제"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"위젯 추가"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"위젯 맞춤설정"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"잠금 화면의 위젯"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"위젯 선택"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"위젯 삭제"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"선택한 위젯 배치"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"사용자 전환"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"풀다운 메뉴"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"출력 설정 열기"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"볼륨 슬라이더 펼침"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"볼륨 슬라이더 접힘"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s 음소거"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s 음소거 해제"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> 재생 위치:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"오디오 재생 위치:"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"전화 거는 중"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index a75d0d5..a224ac7 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Виджеттерди ыңгайлаштыруу үчүн кое бербей басып туруңуз"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Виджеттерди ыңгайлаштыруу"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Өчүрүлгөн виджет үчүн колдонмонун сүрөтчөсү"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Виджетти түзөтүү"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Өчүрүү"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Виджет кошуу"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Виджеттерди ыңгайлаштыруу"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Кулпуланган экрандагы виджеттер"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"виджет тандоо"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"виджетти алып салуу"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"тандалган виджетти жайгаштыруу"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Колдонуучуну которуу"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ылдый түшүүчү меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана аларга байланыштуу нерселер өчүрүлөт."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Чыгаруу параметрлерин киргизүү"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Үндүн катуулугунун сыдырмалары жайып көрсөтүлдү"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Үндүн катуулугунун сыдырмалары жыйыштырылды"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s үнүн басуу"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s үнүн чыгаруу"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> аркылуу ойнотулууда"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудио кайсы жерде ойнотулат:"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Чалууда"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index eca041e..88866e3 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ກົດຄ້າງໄວ້ເພື່ອປັບແຕ່ງວິດເຈັດ"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ປັບແຕ່ງວິດເຈັດ"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"ໄອຄອນແອັບສຳລັບວິດເຈັດທີ່ຖືກປິດການນຳໃຊ້"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"ແກ້ໄຂວິດເຈັດ"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"ລຶບອອກ"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ເພີ່ມວິດເຈັດ"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ປັບແຕ່ງວິດເຈັດ"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ວິດເຈັດຢູ່ໜ້າຈໍລັອກ"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ເລືອກວິດເຈັດ"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ລຶບວິດເຈັດອອກ"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ວາງວິດເຈັດທີ່ເລືອກ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ເມນູແບບດຶງລົງ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯ​ແລະ​ຂໍ້​ມູນ​ທັງ​ໝົດ​ໃນ​ເຊດ​ຊັນ​ນີ້​ຈະ​ຖືກ​ລຶບ​ອອກ."</string>
@@ -629,10 +629,16 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"ໃສ່ການຄັ້ງຄ່າເອົ້າພຸດ"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"ຂະຫຍາຍສະໄລເດີລະດັບສຽງແລ້ວ"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"ຫຍໍ້ສະໄລເດີລະດັບສຽງລົງແລ້ວ"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"ປິດສຽງ %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"ເຊົາປິດສຽງ %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"ກຳລັງຫຼິ້ນ <xliff:g id="LABEL">%s</xliff:g> ໃນ"</string>
-    <string name="media_output_title_without_playing" msgid="3825663683169305013">"ສຽງຈະຫຼິ້ນຕໍ່ໄປ"</string>
+    <string name="media_output_title_without_playing" msgid="3825663683169305013">"ສຽງຈະຫຼິ້ນຢູ່"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"ກຳລັງໂທ"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"ແຖບສະຖານະ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index fa5790c..46ac304 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Ilgai paspauskite, kad tinkintumėte valdiklius"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Tinkinti valdiklius"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Išjungto valdiklio programos piktograma"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Redaguoti valdiklį"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Pašalinti"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pridėti valdiklį"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Tinkinti valdiklius"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Valdikliai užrakinimo ekrane"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"pasirinkite valdiklį"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"pašalinti valdiklį"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"padėti pasirinktą valdiklį"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"išplečiamasis meniu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Įveskite išvesties nustatymus"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Garsumo šliaužikliai išskleisti"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Garsumo šliaužikliai sutraukti"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"nutildyti %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"įjungti garsą %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Leidžiama „<xliff:g id="LABEL">%s</xliff:g>“"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Garsas bus leidžiamas"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Skambinama"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 9e01526..979f59e 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -370,7 +370,7 @@
     <string name="quick_settings_contrast_high" msgid="656049259587494499">"Augsts"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dzirdes aparāti"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dzirdes aparāti"</string>
-    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Savienojiet pārī jaunu ierīci"</string>
+    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Savienot pārī jaunu ierīci"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Noklikšķiniet, lai savienotu pārī jaunu ierīci"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nevarēja atjaunināt pirmsiestatījumu"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vai atbloķēt ierīces mikrofonu?"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Nospiediet un turiet, lai pielāgotu logrīkus."</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Pielāgot logrīkus"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Lietotnes ikona atspējotam logrīkam"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Rediģēt logrīku"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Noņemt"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pievienot logrīku"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Pielāgot logrīkus"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Logrīki bloķēšanas ekrānā"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"atlasīt logrīku"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"noņemt logrīku"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"novietot atlasīto logrīku"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mainīt lietotāju"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"novelkamā izvēlne"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Atvērt izvades iestatījumus"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Skaļuma slīdņi izvērsti"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Skaļuma slīdņi sakļauti"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"izslēgt skaņu straumei %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"ieslēgt skaņu straumei %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> — atskaņošana šeit:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio tiks atskaņots šeit:"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Aktīvs zvans ierīcē"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Sistēmas saskarnes regulators"</string>
     <string name="status_bar" msgid="4357390266055077437">"Statusa josla"</string>
     <string name="demo_mode" msgid="263484519766901593">"Sistēmas lietotāja saskarnes demonstrācijas režīms"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index d1e26e5..e305c9e 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Притиснете долго за да ги приспособите виџетите"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Приспособете ги виџетите"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Икона за апликација за оневозможен виџет"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Изменување виџети"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Отстранува"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Додајте виџет"</string>
@@ -596,14 +598,14 @@
     <string name="screen_pinning_exit" msgid="4553787518387346893">"Апликацијата е откачена"</string>
     <string name="stream_voice_call" msgid="7468348170702375660">"Повик"</string>
     <string name="stream_system" msgid="7663148785370565134">"Систем"</string>
-    <string name="stream_ring" msgid="7550670036738697526">"Ѕвони"</string>
+    <string name="stream_ring" msgid="7550670036738697526">"Ѕвонење"</string>
     <string name="stream_music" msgid="2188224742361847580">"Аудиовизуелни содржини"</string>
     <string name="stream_alarm" msgid="16058075093011694">"Аларм"</string>
     <string name="stream_notification" msgid="7930294049046243939">"Известување"</string>
     <string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string>
     <string name="stream_dtmf" msgid="7322536356554673067">"Двојна повеќетонска фреквенција"</string>
     <string name="stream_accessibility" msgid="3873610336741987152">"Пристапност"</string>
-    <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ѕвони"</string>
+    <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ѕвонење"</string>
     <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вибрации"</string>
     <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Исклучи звук"</string>
     <string name="media_device_cast" msgid="4786241789687569892">"Емитување"</string>
@@ -629,12 +631,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Внесете ги поставките за излез"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Лизгачите за јачина на звукот се проширени"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Лизгачите за јачина на звукот се собрани"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"исклучување звук на %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"вклучување звук на %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>: пуштено на"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудиото ќе се пушти на"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Повик во тек"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Адаптер на УИ на системот"</string>
     <string name="status_bar" msgid="4357390266055077437">"Статусна лента"</string>
     <string name="demo_mode" msgid="263484519766901593">"Демо-режим на кориснички интерфејс на систем"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 79be0f1..c14cc41 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"വിജറ്റുകൾ ഇഷ്ടാനുസൃതമാക്കാൻ ദീർഘനേരം അമർത്തുക"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"വിജറ്റുകൾ ഇഷ്ടാനുസൃതമാക്കുക"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"പ്രവർത്തനരഹിതമാക്കിയ വിജറ്റിനുള്ള ആപ്പ് ഐക്കൺ"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"വിജറ്റ് എഡിറ്റ് ചെയ്യുക"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"നീക്കം ചെയ്യുക"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"വിജറ്റ് ചേർക്കുക"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"വിജറ്റുകൾ ഇഷ്ടാനുസൃതമാക്കുക"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ലോക്ക് സ്‌ക്രീനിൽ വിജറ്റുകൾ"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"വിജറ്റ് തിരഞ്ഞെടുക്കുക"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"വിജറ്റ് നീക്കം ചെയ്യുക"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"തിരഞ്ഞെടുത്ത വിജറ്റ് നൽകുക"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ഉപയോക്താവ് മാറുക"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"പുൾഡൗൺ മെനു"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"ഔട്ട്പുട്ട് ക്രമീകരണം നൽകുക"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"വോളിയം സ്ലൈഡറുകൾ വികസിപ്പിച്ചു"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"വോളിയം സ്ലൈഡറുകൾ ചുരുക്കി"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s മ്യൂട്ട് ചെയ്യുക"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s അൺമ്യൂട്ട് ചെയ്യുക"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യുന്നു"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"ഓഡിയോ പ്ലേ ചെയ്യും"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"കോൾ പുരോഗമിക്കുന്നു"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 93304d6..573c8ff 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Виджетүүдийг өөрчлөхийн тулд удаан дарна уу"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Виджетүүдийг өөрчлөх"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Идэвхгүй болгосон виджетийн аппын дүрс тэмдэг"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Виджетийг засах"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Хасах"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Виджет нэмэх"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Виджетийг өөрчлөх"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Түгжээтэй дэлгэц дээрх виджетүүд"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"виджет сонгох"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"виджетийг хасах"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"сонгосон виджетийг байрлуулах"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"эвхмэл цэс"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ харилцан үйлдлийн бүх апп болон дата устах болно."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Оролтын тохиргоог оруулах"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Дууны түвшний гулсуулагчдыг дэлгэсэн"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Дууны түвшний гулсуулагчдыг хураасан"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s-н дууг хаах"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s-н дууг нээх"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> тоглуулж байна"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудиог дараахад тоглуулна"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Дуудлага хийгдэж буй:"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Системийн UI Тохируулагч"</string>
     <string name="status_bar" msgid="4357390266055077437">"Статус самбар"</string>
     <string name="demo_mode" msgid="263484519766901593">"Системийн UI демо горим"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 0aad7643..3667f1e 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"विजेट कस्टमाइझ करण्यासाठी प्रेस करून ठेवा"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"विजेट कस्टमाइझ करा"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"बंद केलेल्या विजेटच्या अ‍ॅपचे आयकन"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"विजेट संपादित करा"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"काढून टाका"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"विजेट जोडा"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"विजेट कस्टमाइझ करा"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"लॉक स्क्रीनवरील विजेट"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"विजेट निवडा"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"विजेट काढून टाका"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"निवडलेले विजेट ठेवा"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"वापरकर्ता स्विच करा"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनू"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अ‍ॅप्स आणि डेटा हटवला जाईल."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"आउटपुट सेटिंग्ज एंटर करा"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"व्हॉल्यूम स्लायडर विस्तारित केले आहेत"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"व्हॉल्यूम स्लायडर कोलॅप्स केले आहेत"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s म्यूट करा"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s अनम्यूट करा"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> वर प्ले करत आहे"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"यावर ऑडिओ प्ले होईल"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"यावर कॉल करत आहे"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 60c028d..60aedfa 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -275,7 +275,7 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan sambungan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Dihidupkan sekali lagi esok secara automatik"</string>
+    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Dihidupkan lagi esok secara automatik"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Ciri seperti Quick Share dan Find My Device menggunakan Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dihidupkan esok pagi"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Perkongsian Audio"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Tekan lama untuk menyesuaikan widget"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Sesuaikan widget"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ikon apl untuk melumpuhkan widget"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Alih keluar"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tambahkan widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Sesuaikan widget"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widget pada skrin kunci"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"pilih widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"alih keluar widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"letakkan widget dipilih"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Tukar pengguna"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu tarik turun"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Masukkan tetapan output"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Peluncur kelantangan dikembangkan"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Peluncur kelantangan dikuncupkan"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"redamkan %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"nyahredamkan %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Memainkan <xliff:g id="LABEL">%s</xliff:g> pada"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio dimainkan pada"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Membuat panggilan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index dcbf837..b7398d0 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ဝိဂျက်များ စိတ်ကြိုက်လုပ်ရန် ကြာကြာနှိပ်ထားပါ"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ဝိဂျက်များကို စိတ်ကြိုက်လုပ်ရန်"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"ပိတ်ထားသော ဝိဂျက်အတွက် အက်ပ်သင်္ကေတ"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"ဝိဂျက်ပြင်ရန်"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"ဖယ်ရှားရန်"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ဝိဂျက်ထည့်ရန်"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ဝိဂျက်များကို စိတ်ကြိုက်လုပ်ရန်"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"လော့ခ်မျက်နှာပြင်ရှိ ဝိဂျက်များ"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ဝိဂျက် ရွေးရန်"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ဝိဂျက် ဖယ်ရှားရန်"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ရွေးချယ်ထားသော ဝိဂျက်ကို တင်ရန်"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ဆွဲချမီနူး"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string>
@@ -619,7 +619,7 @@
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"ထောင့်စုံအော်ဒီယို"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"ပိတ်"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ပုံသေ"</string>
-    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ခေါင်းလှုပ်ရှားမှု စောင့်ကြည့်ခြင်း"</string>
+    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ခေါင်းလှုပ်ရှားမှု"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"ဖုန်းခေါ်သံမုဒ်သို့ ပြောင်းရန် တို့ပါ"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"အသံပိတ်ရန်"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"အသံဖွင့်ရန်"</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"အထွက် ဆက်တင်များ ထည့်ရန်"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"အသံအတိုးအကျယ် ရွှေ့တုံးများ ပိုပြထားသည်"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"အသံအတိုးအကျယ် ရွှေ့တုံးများ လျှော့ပြထားသည်"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s အသံပိတ်ရန်"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s အသံပြန်ဖွင့်ရန်"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ဖွင့်မည့်နေရာ"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"အသံဖွင့်မည့်နေရာ"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"ဖုန်းဆက်နေသည်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 2724a51..2b131c7 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Trykk lenge for å tilpasse modulene"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Tilpass moduler"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Appikon for deaktivert modul"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Endre modul"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Fjern"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Legg til modul"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Tilpass moduler"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Moduler på låseskjermen"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"velg modul"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"fjern modul"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"plasser den valgte modulen"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullegardinmeny"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apper og data i denne økten blir slettet."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Angi utdatainnstillinger"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Glidebrytere for volum er skjult"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Glidebrytere for volum er skjult"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"kutt lyden til %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"slå på lyden til %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Spiller av <xliff:g id="LABEL">%s</xliff:g> på"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Lyden spilles av på"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Aktiv samtale på"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Statusrad"</string>
     <string name="demo_mode" msgid="263484519766901593">"Demomodus for systemgrensesnitt"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 1bbab4a..42abd44 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -203,10 +203,10 @@
     <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"फेस अनलक सेटअप गर्न सकिएन। फेरि प्रयास गर्न सेटिङमा जानुहोस्।"</string>
     <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"फिंगरप्रिन्ट सेन्सरमा छुनुहोस्‌"</string>
     <string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"जारी राख्न अनलक आइकनमा थिच्नुहोस्"</string>
-    <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5542430577183894219">"अनुहार पहिचान गर्न सकिएन। बरु फिंगरप्रिन्ट प्रयोग गर्नुहोस्।"</string>
+    <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5542430577183894219">"अनुहार मिलेन। बरु फिंगरप्रिन्ट प्रयोग गर्नुहोस्।"</string>
     <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
     <skip />
-    <string name="keyguard_face_failed" msgid="2346762871330729634">"अनुहार पहिचान गर्न सकिएन"</string>
+    <string name="keyguard_face_failed" msgid="2346762871330729634">"अनुहार मिलेन"</string>
     <string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"बरु फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string>
     <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"फेस अनलक उपलब्ध छैन"</string>
     <string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ब्लुटुथ जडान भयो।"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"विजेटहरू कस्टमाइज गर्न केही बेरसम्म थिच्नुहोस्"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"विजेटहरू कस्टमाइज गर्नुहोस्"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"अफ गरिएको विजेटको एप जनाउने आइकन"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"विजेट सम्पादन गर्नुहोस्"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"हटाउनुहोस्"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"विजेट हाल्नुहोस्"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"विजेटहरू कस्टमाइज गर्नुहोस्"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"लक स्क्रिनमा भएका विजेटहरू"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"विजेट चयन गर्नुहोस्"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"विजेट हटाउनुहोस्"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"चयन गरिएका विजेटका लागि ठाउँ चयन गर्नुहोस्"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"प्रयोगकर्ता फेर्नुहोस्"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनु"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यो सत्रमा भएका सबै एपहरू र डेटा मेटाइने छ।"</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"आउटपुटसम्बन्धी सेटिङमा जानुहोस्"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"भोल्युम स्लाइडरहरू एक्स्पान्ड गरिएका छन्"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"भोल्युम स्लाइडरहरू कोल्याप्स गरिएका छन्"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s म्युट गर्नुहोस्"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s अनम्युट गर्नुहोस्"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> प्ले गरिँदै छ"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"अडियो यसमा प्ले हुने छ"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"कल चलिरहेको छ"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 1a831ac..c459b93 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Houd lang ingedrukt om widgets aan te passen"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Widgets aanpassen"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App-icoon voor uitgezette widget"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Widget bewerken"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Verwijderen"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget toevoegen"</string>
@@ -619,7 +621,7 @@
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Ruimtelijke audio"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Uit"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Vast"</string>
-    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofd­beweging volgen"</string>
+    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofdtracking"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Tik om de beltoonmodus te wijzigen"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"geluid uit"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"geluid aanzetten"</string>
@@ -629,8 +631,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Uitvoerinstellingen invoeren"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Volumeschuifregelaars uitgevouwen"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Volumeschuifregelaars samengevouwen"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"geluid van %s uitzetten"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"geluid van %s aanzetten"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> wordt afgespeeld op"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio wordt afgespeeld op"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Bellen actief"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index e64b1b4..1444e6d 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -370,7 +370,7 @@
     <string name="quick_settings_contrast_high" msgid="656049259587494499">"ଅଧିକ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ହିଅରିଂ ଡିଭାଇସଗୁଡ଼ିକ"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ହିଅରିଂ ଡିଭାଇସଗୁଡ଼ିକ"</string>
-    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ନୂଆ ଡିଭାଇସ ପେୟାର କର"</string>
+    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ନୂଆ ଡିଭାଇସ ପେୟାର କରନ୍ତୁ"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ନୂଆ ଡିଭାଇସ ପେୟାର କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ପ୍ରିସେଟକୁ ଅପଡେଟ କରାଯାଇପାରିଲା ନାହିଁ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ଡିଭାଇସର ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ କରିବେ?"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ୱିଜେଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରିବା ପାଇଁ ଅଧିକ ସମୟ ଦବାନ୍ତୁ"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ୱିଜେଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"ଅକ୍ଷମ କରାଯାଇଥିବା ୱିଜେଟ ପାଇଁ ଆପ ଆଇକନ"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"ୱିଜେଟକୁ ଏଡିଟ କରନ୍ତୁ"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ୱିଜେଟ ଯୋଗ କରନ୍ତୁ"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ୱିଜେଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ଲକ ସ୍କ୍ରିନରେ ଥିବା ୱିଜେଟଗୁଡ଼ିକ"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ୱିଜେଟ ଚୟନ କରନ୍ତୁ"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ୱିଜେଟକୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ଚୟନିତ ୱିଜେଟ ରଖନ୍ତୁ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍‍ ବଦଳାନ୍ତୁ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ପୁଲଡାଉନ ମେନୁ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍‌ ଓ ଡାଟା ଡିଲିଟ୍‌ ହୋଇଯିବ।"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"ଆଉଟପୁଟ ସେଟିଂସ ଲେଖନ୍ତୁ"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"ଭଲ୍ୟୁମ ସ୍ଲାଇଡରଗୁଡ଼ିକୁ ବିସ୍ତାର କରାଯାଇଛି"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"ଭଲ୍ୟୁମ ସ୍ଲାଇଡରଗୁଡ଼ିକୁ ସଙ୍କୁଚିତ କରାଯାଇଛି"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%sକୁ ମ୍ୟୁଟ କରନ୍ତୁ"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%sକୁ ଅନମ୍ୟୁଟ କରନ୍ତୁ"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>ରେ ପ୍ଲେ କରାଯାଉଛି"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"ଅଡିଓ ଏଥିରେ ପ୍ଲେ ହେବ"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"କଲ ଚାଲିଛି"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"ସିଷ୍ଟମ୍ UI ଟ୍ୟୁନର୍‍"</string>
     <string name="status_bar" msgid="4357390266055077437">"ଷ୍ଟାଟସ୍‍ ବାର୍‍"</string>
     <string name="demo_mode" msgid="263484519766901593">"ସିଷ୍ଟମ୍‌ UI ଡେମୋ ମୋଡ୍‌"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 394d790..23914b3 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ਵਿਜੇਟਾਂ ਨੂੰ ਵਿਉਂਤਬੱਧ ਕਰਨ ਲਈ ਦਬਾਈ ਰੱਖੋ"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ਵਿਜੇਟ ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"ਬੰਦ ਵਿਜੇਟ ਲਈ ਐਪ ਪ੍ਰਤੀਕ"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"ਵਿਜੇਟ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"ਹਟਾਓ"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ਵਿਜੇਟ ਸ਼ਾਮਲ ਕਰੋ"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ਵਿਜੇਟ ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ਵਿਜੇਟ ਚੁਣੋ"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ਵਿਜੇਟ ਹਟਾਓ"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ਚੁਣੇ ਗਏ ਵਿਜੇਟ ਲਈ ਥਾਂ ਚੁਣੋ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ਪੁੱਲਡਾਊਨ ਮੀਨੂ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿਚਲੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟੇ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"ਆਊਟਪੁੱਟ ਸੈਟਿੰਗਾਂ ਦਾਖਲ ਕਰੋ"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"ਅਵਾਜ਼ ਸਲਾਈਡਰਾਂ ਵਿਸਤਾਰ ਕੀਤਾ ਗਿਆ"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"ਅਵਾਜ਼ ਸਲਾਈਡਰਾਂ ਨੂੰ ਸਮੇਟਿਆ ਗਿਆ"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s ਨੂੰ ਮਿਊਟ ਕਰੋ"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s ਨੂੰ ਅਣਮਿਊਟ ਕਰੋ"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ਚਲਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"ਆਡੀਓ ਇਸ \'ਤੇ ਚੱਲੇਗੀ"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"ਕਾਲ ਜਾਰੀ ਹੈ"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI ਟਿਊਨਰ"</string>
     <string name="status_bar" msgid="4357390266055077437">"ਸਥਿਤੀ ਪੱਟੀ"</string>
     <string name="demo_mode" msgid="263484519766901593">"ਸਿਸਟਮ UI ਡੈਮੋ ਮੋਡ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 9208628..4609f08 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Przytrzymaj, aby dostosować widżety"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Dostosuj widżety"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ikona aplikacji z wyłączonym widżetem"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Edytuj widżet"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Usuń"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj widżet"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Dostosuj widżety"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widżety na ekranie blokady"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"wybierz widżet"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"usuń widżet"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"umieść wybrany widżet"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Otwórz ustawienia sygnału wyjściowego"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Suwaki głośności są rozwinięte"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Suwaki głośności są zwinięte"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"wycisz: %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"wyłącz wyciszenie: %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Odtwarzam <xliff:g id="LABEL">%s</xliff:g> na"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Wyjścia dźwięku:"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Dzwonię na:"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Kalibrator System UI"</string>
     <string name="status_bar" msgid="4357390266055077437">"Pasek stanu"</string>
     <string name="demo_mode" msgid="263484519766901593">"Tryb demonstracyjny interfejsu"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 60472c8..425fa65 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha pressionado para personalizar widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ícone do app para widget desativado"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Editar widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Remover"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Personalizar widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets na tela de bloqueio"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"selecionar widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remover widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"posicionar widget selecionado"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Inserir configurações de saída"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Controles deslizantes de volume abertos"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Controles deslizantes de volume fechados"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"desativar o som de %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"ativar o som de %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
     <string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da interface do sistema"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 3b9bebb..4453133 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha premido para personalizar os widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ícone da app do widget desativado"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Editar widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Remover"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Personalizar widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets no ecrã de bloqueio"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"selecionar widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remover widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"posicionar widget selecionado"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mudar utilizador"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pendente"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as apps e dados desta sessão serão eliminados."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Introduzir definições de saída"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Controlos de deslize do volume expandidos"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Controlos de deslize do volume reduzidos"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"desativar o som de %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"reativar o som de %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"A ouvir <xliff:g id="LABEL">%s</xliff:g> em:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Áudio ouvido em:"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Chamada em curso"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 60472c8..425fa65 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha pressionado para personalizar widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ícone do app para widget desativado"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Editar widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Remover"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Personalizar widgets"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgets na tela de bloqueio"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"selecionar widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remover widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"posicionar widget selecionado"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Inserir configurações de saída"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Controles deslizantes de volume abertos"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Controles deslizantes de volume fechados"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"desativar o som de %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"ativar o som de %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
     <string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da interface do sistema"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 84d68e70..596349e 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Apasă lung pentru a personaliza widgeturi"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizează widgeturile"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Pictograma aplicației pentru widgetul dezactivat"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Editează widgetul"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Elimină"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adaugă un widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Personalizează widgeturile"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgeturi pe ecranul de blocare"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"selectează un widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"elimină widgetul"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"plasează widgetul selectat"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Introdu setările de ieșire"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Glisoare de volum extinse"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Glisoare de volum restrânse"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"dezactivează sunetul pentru %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"activează sunetul pentru %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Se redă <xliff:g id="LABEL">%s</xliff:g> pe"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Conținutul audio se va reda pe"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Apel în curs pe"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Bară de stare"</string>
     <string name="demo_mode" msgid="263484519766901593">"Mod demonstrativ pentru IU sistem"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index c405469..6fabeae 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Нажмите и удерживайте, чтобы настроить виджеты."</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Настроить виджеты"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Значок приложения для отключенного виджета"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Изменить виджет"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Удалить"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Добавить виджет"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Настроить виджеты"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Виджеты на заблокированном экране"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"выбрать виджет"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"удалить виджет"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"разместить выбранный виджет"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Сменить пользователя."</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"раскрывающееся меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Перейти к настройкам вывода аудио"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Ползунки для регулировки громкости развернуты"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Ползунки для регулировки громкости свернуты"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"отключить звук: %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"включить звук: %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> – запущено здесь:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Проигрывание аудио:"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Настройки вызова"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Строка состояния"</string>
     <string name="demo_mode" msgid="263484519766901593">"Интерфейс системы: деморежим"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 37bb23a..238e6c8 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"විජට් අභිරුචිකරණය කිරීමට දිගු ඔබන්න"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"විජට්ටු අභිරුචි කරන්න"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"අබල කළ විජට් සඳහා යෙදුම් නිරූපකය"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"විජට්ටු සංස්කරණ කරන්න"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"ඉවත් කරන්න"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"විජට්ටුව එක් කරන්න"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"විජට්ටු අභිරුචි කරන්න"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"අගුළු තිරයෙහි විජට්"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"විජට්ටුව තෝරන්න"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"විජට්ටුව ඉවත් කරන්න"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"තෝරන ලද විජට්ටුව තබන්න"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"පරිශීලක මාරුව"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"නිපතන මෙනුව"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"ප්‍රතිදාන සැකසීම් ඇතුල් කරන්න"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"හඬ ස්ලයිඩර දිගහැර ඇත"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"හඬ ස්ලයිඩර හකුළා ඇත"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s නිහඬ කරන්න"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s නිහඬ නොකරන්න"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> වාදනය කරන්නේ"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"ශ්‍රව්‍ය වාදනය වනු ඇත්තේ"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"ඇමතීම"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"පද්ධති UI සුසරකය"</string>
     <string name="status_bar" msgid="4357390266055077437">"තත්ත්ව තීරුව"</string>
     <string name="demo_mode" msgid="263484519766901593">"පද්ධති UI ආදර්ශන ප්‍රකාරය"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 675a345..825dba6 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -269,7 +269,7 @@
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Klepnutím pripojíte alebo odpojíte zariadenie"</string>
     <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Spárovať nové zariadenie"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Zobraziť všetko"</string>
-    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Použiť Bluetooth"</string>
+    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Používať Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Pripojené"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Zdieľanie zvuku"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uložené"</string>
@@ -370,7 +370,7 @@
     <string name="quick_settings_contrast_high" msgid="656049259587494499">"Vysoký"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Načúvacie zariadenia"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Načúvacie zariadenia"</string>
-    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Párovanie nového zariadenia"</string>
+    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovať nové zariadenie"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zariadenie"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Predvoľbu sa nepodarilo aktualizovať"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Chcete odblokovať mikrofón zariadenia?"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Miniaplikácie prispôsobíte dlhým stlačením"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prispôsobiť miniaplikácie"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ikona deaktivovanej miniaplikácie"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Upraviť miniaplikáciu"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Odstrániť"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pridať miniaplikáciu"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Prispôsobiť miniaplikácie"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Miniaplikácie na uzamknutej obrazovke"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vybrať miniaplikáciu"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"odstrániť miniaplikáciu"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"prepnúť vybranú miniaplikáciu"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbaľovacia ponuka"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string>
@@ -619,7 +619,7 @@
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Priestorový zvuk"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Vypnuté"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Pevné"</string>
-    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sled. pohybov hlavy"</string>
+    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sled. polohy hlavy"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Režim zvonenia zmeníte klepnutím"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnite zvuk"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnite zvuk"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Zadať nastavenia výstupu"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Posúvače hlasitosti sú rozbalené"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Posúvače hlasitosti sú zbalené"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"vypnete zvuk %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"zapnete zvuk %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> sa prehráva v:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk sa prehrá cez"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Volanie je zapnuté"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Tuner používateľského rozhrania systému"</string>
     <string name="status_bar" msgid="4357390266055077437">"Stavový riadok"</string>
     <string name="demo_mode" msgid="263484519766901593">"Ukážka používateľského rozhrania systému"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index f97d662..82f2df7 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pridržite za prilagajanje pripomočkov"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prilagajanje pripomočkov"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ikona aplikacije za onemogočen pripomoček"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Urejanje pripomočka"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Odstrani"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodajanje pripomočka"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Prilagajanje pripomočkov"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Pripomočki na zaklenjenem zaslonu"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"izberite pripomoček"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"odstranitev pripomočka"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavitev izbranega pripomočka"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Preklop med uporabniki"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"spustni meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Vnos izhodnih nastavitev"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Razširitev drsnikov za glasnost"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Strnitev drsnikov za glasnost"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"izklop zvoka %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"vklop zvoka %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Predvajanje »<xliff:g id="LABEL">%s</xliff:g>« v"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvok bo predvajan v"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Poteka klicanje"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Uglaševalnik uporabniškega vmesnika sistema"</string>
     <string name="status_bar" msgid="4357390266055077437">"Vrstica stanja"</string>
     <string name="demo_mode" msgid="263484519766901593">"Predstavitveni način uporabniškega vmesnika sistema"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index b638198..af2e3c7 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Shtyp gjatë për të personalizuar miniaplikacionet"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizo miniaplikacionet"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ikona e aplikacionit për miniaplikacionin e çaktivizuar"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Modifiko miniaplikacionin"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Hiq"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Shto miniaplikacionin"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Personalizo miniaplikacionet"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Miniaplikacionet në ekranin e kyçjes"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"zgjidh miniaplikacionin"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"hiq miniaplikacionin"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"vendos miniaplikacionin e zgjedhur"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyja me tërheqje poshtë"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Hyr te cilësimet e daljes"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Rrëshqitësit e volumit u zgjeruan"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Rrëshqitësit e volumit u palosën"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"çaktivizo audion: %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"aktivizo audion: %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Po luhet <xliff:g id="LABEL">%s</xliff:g> te"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Do të luhet audio te"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Telefonatë aktive"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit"</string>
     <string name="status_bar" msgid="4357390266055077437">"Shiriti i statusit"</string>
     <string name="demo_mode" msgid="263484519766901593">"Modaliteti i demonstrimit i ndërfaqes së përdoruesit të sistemit"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 4cc1134..42b979c 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Дуги притисак за прилагођавање виџета"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Прилагоди виџете"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Икона апликације за онемогућен виџет"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Измени виџет"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Уклони"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Додај виџет"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Прилагодите виџете"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Виџети на закључаном екрану"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"изаберите виџет"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"уклоните виџет"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"поставите изабрани виџет"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падајући мени"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Унесите подешавања излазног сигнала"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Клизачи за јачину звука су проширени"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Клизачи за јачину звука су скупљени"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"искључите звук за: %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"укључите звук за: %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> се пушта на"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Звук се пушта на"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Позив на уређају"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Тјунер за кориснички интерфејс система"</string>
     <string name="status_bar" msgid="4357390266055077437">"Статусна трака"</string>
     <string name="demo_mode" msgid="263484519766901593">"Режим демонстрације за кориснички интерфејс система"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index b0ce12f..29dca46 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Tryck länge för att anpassa widgetar"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Anpassa widgetar"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Appikon för inaktiverad widget"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Redigera widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Ta bort"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lägg till widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Anpassa widgetar"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Widgetar på låsskärmen"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"välj widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ta bort widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"placera vald widget"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullgardinsmeny"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Ange inställningar för utdata"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Volymreglagen har utökats"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Volymreglagen har komprimerats"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"stäng av ljudet för %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"slå på ljudet för %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Spelar upp <xliff:g id="LABEL">%s</xliff:g> på"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Ljud spelas upp på"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Samtal på"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Inställningar för systemgränssnitt"</string>
     <string name="status_bar" msgid="4357390266055077437">"Statusfält"</string>
     <string name="demo_mode" msgid="263484519766901593">"Demoläge för systemgränssnitt"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 8dc22a7..9d5181e 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Bonyeza kwa muda mrefu uweke mapendeleo ya wijeti"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Badilisha wijeti upendavyo"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Aikoni ya programu ya wijeti iliyozimwa"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Badilisha wijeti"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Ondoa"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ongeza wijeti"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Badilisha wijeti upendavyo"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Wijeti kwenye skrini iliyofungwa"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"chagua wijeti"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ondoa wijeti"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"weka wijeti uliyochagua"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Badili mtumiaji"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyu ya kuvuta chini"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Weka mipangilio ya sauti inayotoka"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Vitelezi vya sauti vimepanuliwa"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Vitelezi vya kiwango cha sauti vimekunjwa"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"zima sauti ya %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"rejesha sauti ya %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Inacheza <xliff:g id="LABEL">%s</xliff:g> kwenye"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Sauti itacheza kwenye"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Simu inaendelea"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Kirekebishi cha kiolesura cha mfumo"</string>
     <string name="status_bar" msgid="4357390266055077437">"Sehemu ya kuonyesha hali"</string>
     <string name="demo_mode" msgid="263484519766901593">"Hali ya onyesho la kirekebishi cha kiolesura cha mfumo"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index ee86aca..a2f4ce5 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -275,7 +275,7 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"சேமிக்கப்பட்டது"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"இணைப்பு நீக்கும்"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"செயல்படுத்தும்"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"நாளைக்குத் தானாகவே மீண்டும் இயக்கப்படும்"</string>
+    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"நாளைக்குத் தானாகவே மீண்டும் இயக்கப்படுதல்"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்கள் புளூடூத்தைப் பயன்படுத்துகின்றன"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"நாளை காலை புளூடூத் இயக்கப்படும்"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ஆடியோ பகிர்வு"</string>
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"விட்ஜெட்களைப் பிரத்தியேகமாக்க நீண்ட நேரம் அழுத்துக"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"விட்ஜெட்களைப் பிரத்தியேகமாக்குங்கள்"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"முடக்கப்பட்ட விட்ஜெட்டுக்கான ஆப்ஸ் ஐகான்"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"விட்ஜெட்டைத் திருத்து"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"அகற்றும்"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"விட்ஜெட்டைச் சேர்"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"விட்ஜெட்களைப் பிரத்தியேகமாக்கும்"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"பூட்டுத் திரையில் விட்ஜெட்கள்"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"விட்ஜெட்டைத் தேர்ந்தெடுக்கும்"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"விட்ஜெட்டை அகற்றும்"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"தேர்ந்தெடுத்த விட்ஜெட்டைக் காட்சிப்படுத்தும்"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"பயனரை மாற்று"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"கீழ் இழுக்கும் மெனு"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா ஆப்ஸும் தரவும் நீக்கப்படும்."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"வெளியீட்டு அமைப்புகளுக்குச் செல்லும்"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"ஒலியளவு ஸ்லைடர்கள் விரிவாக்கப்பட்டன"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"ஒலியளவு ஸ்லைடர்கள் சுருக்கப்பட்டன"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s ஐ ஒலியடக்கும்"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s ஐ ஒலி இயக்கும்"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"இதில் <xliff:g id="LABEL">%s</xliff:g> பிளே ஆகிறது"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"ஆடியோ இதில் பிளே ஆகும்"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"அழைப்பில் உள்ளது"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"நிலைப் பட்டி"</string>
     <string name="demo_mode" msgid="263484519766901593">"சிஸ்டம் பயனர் இடைமுக டெமோ பயன்முறை"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index d8622a3..5b703a0 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"విడ్జెట్‌లను అనుకూలీకరించడానికి, నొక్కి, ఉంచండి"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"విడ్జెట్‌లను అనుకూలంగా మార్చండి"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"డిజేబుల్ చేయబడిన విడ్జెట్ కోసం యాప్ చిహ్నం"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"విడ్జెట్‌ను ఎడిట్ చేయండి"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"తీసివేయండి"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"విడ్జెట్‌ను జోడించండి"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"విడ్జెట్‌లను అనుకూలంగా మార్చండి"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"లాక్ స్క్రీన్‌లో విడ్జెట్‌లు"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"విడ్జెట్‌ను ఎంచుకోండి"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"విడ్జెట్‌ను తీసివేయండి"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ఎంచుకున్న విడ్జెట్ కోసం ప్లేస్‌ను ఎంచుకోండి"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"వినియోగదారుని మార్చు"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"పుల్‌డౌన్ మెనూ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్‌లోని అన్ని యాప్‌లు మరియు డేటా తొలగించబడతాయి."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"అవుట్‌పుట్ సెట్టింగ్‌లను ఎంటర్ చేయండి"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"వాల్యూమ్ స్లయిడర్‌లు విస్తరించబడ్డాయి"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"వాల్యూమ్ స్లయిడర్‌లు కుదించబడ్డాయి"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%sను మ్యూట్ చేయండి"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%sను అన్‌మ్యూట్ చేయండి"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>‌ ప్లే అయ్యే డివైజ్"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"ఆడియో ప్లే డివైజ్"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"కాల్ ప్రోగ్రెస్‌లో ఉంది"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"సిస్టమ్ UI ట్యూనర్"</string>
     <string name="status_bar" msgid="4357390266055077437">"స్టేటస్‌ బార్‌"</string>
     <string name="demo_mode" msgid="263484519766901593">"సిస్టమ్ UI డెమో మోడ్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 9f09b9c..73ca200 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"กดค้างเพื่อปรับแต่งวิดเจ็ต"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ปรับแต่งวิดเจ็ต"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"ไอคอนแอปสำหรับวิดเจ็ตที่ปิดใช้อยู่"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"แก้ไขวิดเจ็ต"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"นำออก"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"เพิ่มวิดเจ็ต"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ปรับแต่งวิดเจ็ต"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"วิดเจ็ตในหน้าจอล็อก"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"เลือกวิดเจ็ต"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"นำวิดเจ็ตออก"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"จัดวางวิดเจ็ตที่เลือก"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"สลับผู้ใช้"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"เมนูแบบเลื่อนลง"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"เข้าสู่การตั้งค่าเอาต์พุต"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"ขยายแถบเลื่อนระดับเสียงแล้ว"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"ยุบแถบเลื่อนระดับเสียงแล้ว"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"ปิดเสียง%s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"เปิดเสียง%s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"กำลังเล่น <xliff:g id="LABEL">%s</xliff:g> ใน"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"เสียงจะเล่นต่อใน"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"กำลังโทรติดต่อ"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 9122627..0c616fa 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pindutin nang matagal para i-customize ang mga widget"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"I-customize ang mga widget"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Icon ng app para sa na-disable na widget"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"I-edit ang widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Alisin"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Magdagdag ng widget"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"I-customize ang mga widget"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Mga widget sa lock screen"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"pumili ng widget"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"alisin ang widget"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ilagay ang napiling widget"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Magpalit ng user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Pumunta sa mga setting ng output"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Na-expand ang mga slider ng volume"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Na-collapse ang mga slider ng volume"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"i-mute ang %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"i-unmute ang %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Nagpe-play ang <xliff:g id="LABEL">%s</xliff:g> sa"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"I-play ang audio sa"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Tumatawag sa"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 1f7a8e0..6723c42 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Widget\'ları özelleştirmek için uzun basın"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Widget\'ları özelleştir"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Devre dışı bırakılan widget\'ın uygulama simgesi"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Widget\'ı düzenle"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Kaldır"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget ekle"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Widget\'ları özelleştir"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Kilit ekranındaki widget\'lar"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"widget seçin"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"widget\'ı kaldır"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"seçilen widget\'ı yerleştir"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kullanıcı değiştirme"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"açılır menü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Çıkış ayarlarını gir"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Ses seviyesi kaydırma çubukları genişletildi"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Ses seviyesi kaydırma çubukları daraltıldı"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%s sesini kapatma"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s sesini açma"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> şurada çalacak:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Ses şurada çalacak:"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Şu cihaz aranıyor:"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Sistem Arayüzü Ayarlayıcısı"</string>
     <string name="status_bar" msgid="4357390266055077437">"Durum çubuğu"</string>
     <string name="demo_mode" msgid="263484519766901593">"Sistem kullanıcı arayüzü demo modu"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index e9b45bf..2659f9f 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Утримуйте, щоб налаштувати віджети"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Налаштувати віджети"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Значок додатка для вимкненого віджета"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Редагувати віджет"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Видалити"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Додати віджет"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Налаштувати віджети"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Віджети на заблокованому екрані"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"виберіть віджет"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"видалити віджет"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"розмістити вибраний віджет"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Змінити користувача"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"спадне меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Відкрити налаштування відтворення"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Повзунки гучності розгорнуто"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Повзунки гучності згорнуто"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"вимкнути звук %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"увімкнути звук %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Де відтворюється <xliff:g id="LABEL">%s</xliff:g>:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Де гратиме аудіо:"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Триває виклик"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Рядок стану"</string>
     <string name="demo_mode" msgid="263484519766901593">"Демо-режим інтерфейсу системи"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index f9fc9f0..2a7fda1 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ویجٹس کو حسب ضرورت بنانے کے لیے لانگ پریس کریں"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ویجیٹس کو حسب ضرورت بنائیں"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"غیر فعال ویجیٹ کے لئے ایپ آئیکن"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"ویجیٹ میں ترمیم کریں"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"ہٹائیں"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ویجیٹ شامل کریں"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"ویجیٹس کو حسب ضرورت بنائیں"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"مقفل اسکرین پر ویجیٹس"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ویجیٹ منتخب کریں"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ویجیٹ ہٹائیں"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"منتخب ویجیٹ رکھیں"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"صارف سوئچ کریں"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"پل ڈاؤن مینیو"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"آؤٹ پٹ کی ترتیبات درج کریں"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"والیوم سلائیڈرز کو پھیلا دیا گیا"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"والیوم سلائیڈرز سکیڑا گیا"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"‏%s خاموش کریں"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"‏%s غیر خاموش کریں"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> پر چل رہی ہے"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"آڈیو اس پر چلے گی"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"کال کی جا رہی ہے"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"‏سسٹم UI ٹیونر"</string>
     <string name="status_bar" msgid="4357390266055077437">"اسٹیٹس بار"</string>
     <string name="demo_mode" msgid="263484519766901593">"‏سسٹم UI ڈیمو موڈ"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 60baeec..0d7709c 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Vidjetlarni sozlash uchun bosib turing"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Vidjetlarni moslashtirish"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Faolsizlantirilgan vidjet uchun ilova belgisi"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Vidjetni tahrirlash"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Olib tashlash"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Vidjet kiritish"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Vidjetlarni moslash"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Ekran qulfidagi vidjetlar"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vidjet tanlash"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"vidjetni olib tashlash"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"tanlangan vidjetni joylash"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Foydalanuvchini almashtirish"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"tortib tushiriladigan menyu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Chiqarish sozlamalarini kiritish"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Tovush slayderlari yoyilgan"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Tovush slayderlari yigʻilgan"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"%sni ovozsiz qilish"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"%s ovozini chiqarish"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>da ijro etilmoqda"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio ijro etiladi"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Chaqiruv yoniq"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 9656eb3..2ddcd6d 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Nhấn và giữ để tuỳ chỉnh tiện ích"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Tuỳ chỉnh tiện ích"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Biểu tượng ứng dụng của tiện ích đã bị vô hiệu hoá"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Chỉnh sửa tiện ích"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Xoá"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Thêm tiện ích"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Tuỳ chỉnh tiện ích"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Các tiện ích trên màn hình khoá"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"chọn tiện ích"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"xoá tiện ích"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"đặt tiện ích đã chọn vào vị trí"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Chuyển đổi người dùng"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"trình đơn kéo xuống"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Mở phần cài đặt thiết bị đầu ra"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Đã mở rộng thanh trượt âm lượng"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Đã thu gọn thanh trượt âm lượng"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"tắt tiếng %s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"bật tiếng %s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Đang phát <xliff:g id="LABEL">%s</xliff:g> trên"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Âm thanh sẽ phát trên"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Đang gọi điện"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Bộ điều hướng giao diện người dùng hệ thống"</string>
     <string name="status_bar" msgid="4357390266055077437">"Thanh trạng thái"</string>
     <string name="demo_mode" msgid="263484519766901593">"Chế độ thử nghiệm giao diện người dùng hệ thống"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 6f1a0bd0..64e37ce 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"长按即可自定义微件"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"自定义微件"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"已停用微件的应用图标"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"修改微件"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"移除"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"添加微件"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"自定义微件"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"锁定屏幕上的微件"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"选择微件"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除微件"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所选微件"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切换用户"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉菜单"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"进入输出设置"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"音量滑块已展开"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"音量滑块已收起"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"静音“%s”"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"取消静音“%s”"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>播放位置:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"音频播放位置:"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"正在通话"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"系统界面调节工具"</string>
     <string name="status_bar" msgid="4357390266055077437">"状态栏"</string>
     <string name="demo_mode" msgid="263484519766901593">"系统界面演示模式"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 47d0692..714e479 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長按即可自訂小工具"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"自訂小工具"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"已停用小工具的應用程式圖示"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"編輯小工具"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"移除"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"新增小工具"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"自訂小工具"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"上鎖畫面上的小工具"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"揀小工具"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除小工具"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所選小工具"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"輸入輸出設定"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"打開咗音量滑桿"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"閂埋咗音量滑桿"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"將%s設定為靜音"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"取消%s的靜音設定"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"正在播放「<xliff:g id="LABEL">%s</xliff:g>」的裝置:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"音訊播放媒體"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"通話中"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"系統使用者介面調諧器"</string>
     <string name="status_bar" msgid="4357390266055077437">"狀態列"</string>
     <string name="demo_mode" msgid="263484519766901593">"系統使用者介面示範模式"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index b3ed7a0..495a055 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長按即可自訂小工具"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"自訂小工具"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"所停用小工具的應用程式圖示"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"編輯小工具"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"移除"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"新增小工具"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"自訂小工具"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"螢幕鎖定畫面上的小工具"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"選取小工具"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除小工具"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所選小工具"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會刪除。"</string>
@@ -629,12 +629,17 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"進入輸出設定"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"音量滑桿已展開"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"音量滑桿已收合"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"將%s設為靜音"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"將%s取消靜音"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"正在播放「<xliff:g id="LABEL">%s</xliff:g>」的裝置:"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"音訊播放位置"</string>
-    <!-- no translation found for media_output_title_ongoing_call (208426888064112006) -->
-    <skip />
+    <string name="media_output_title_ongoing_call" msgid="208426888064112006">"通話中"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"系統使用者介面調整精靈"</string>
     <string name="status_bar" msgid="4357390266055077437">"狀態列"</string>
     <string name="demo_mode" msgid="263484519766901593">"系統 UI 展示模式"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 6e9e08b..f5ee5a0 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -447,6 +447,8 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Cindezela isikhathi eside ukuze wenze ngokwezifiso amawijethi"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Yenza ngokwezifiso amawijethi"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Isithonjana se-app sewijethi evaliwe"</string>
+    <!-- no translation found for icon_description_for_pending_widget (8413816401868001755) -->
+    <skip />
     <string name="edit_widget" msgid="9030848101135393954">"Hlela amawijethi"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Susa"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Engeza iwijethi"</string>
@@ -461,10 +463,8 @@
     <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Yenza ngokwezifiso amawijethi"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Amawijethi ekukhiyeni isikrini"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"khetha iwijethi"</string>
-    <!-- no translation found for accessibility_action_label_remove_widget (3373779447448758070) -->
-    <skip />
-    <!-- no translation found for accessibility_action_label_place_widget (1914197458644168978) -->
-    <skip />
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"susa iwijethi"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"beka iwijethi ekhethiwe"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Shintsha umsebenzisi"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"imenyu yokudonsela phansi"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wonke ama-app nedatha kulesi sikhathi azosuswa."</string>
@@ -629,8 +629,14 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"Faka amasethingi wokuphumayo"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"Izilayidi zevolumu zinwetshiwe"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"Izilayidi zevolumu zigoqiwe"</string>
-    <string name="volume_panel_hint_mute" msgid="6962563028495243738">"thulisa i-%s"</string>
-    <string name="volume_panel_hint_unmute" msgid="7489063242934477382">"susa ukuthula kwe-%s"</string>
+    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_muted (1124844870181285320) -->
+    <skip />
+    <!-- no translation found for volume_panel_hint_vibrate (4136223145435914132) -->
+    <skip />
     <string name="media_output_label_title" msgid="872824698593182505">"Idlala ku-<xliff:g id="LABEL">%s</xliff:g>"</string>
     <string name="media_output_title_without_playing" msgid="3825663683169305013">"Umsindo uzodlala"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ifonela kokuthi"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5857692..9d0319c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -449,6 +449,8 @@
     <dimen name="overlay_preview_container_margin">8dp</dimen>
     <dimen name="overlay_action_container_margin_horizontal">8dp</dimen>
     <dimen name="overlay_action_container_margin_bottom">6dp</dimen>
+    <!-- minimum distance to the left, right or bottom edges. -->
+    <dimen name="overlay_action_container_minimum_edge_spacing">12dp</dimen>
     <dimen name="overlay_bg_protection_height">242dp</dimen>
     <dimen name="overlay_action_container_corner_radius">20dp</dimen>
     <dimen name="overlay_action_container_padding_vertical">8dp</dimen>
@@ -912,8 +914,8 @@
     <dimen name="communal_enforced_rounded_corner_max_radius">28dp</dimen>
 
     <!-- Width and height used to filter widgets displayed in the communal widget picker -->
-    <dimen name="communal_widget_picker_desired_width">424dp</dimen>
-    <dimen name="communal_widget_picker_desired_height">282dp</dimen>
+    <dimen name="communal_widget_picker_desired_width">360dp</dimen>
+    <dimen name="communal_widget_picker_desired_height">240dp</dimen>
 
     <!-- The width/height of the unlock icon view on keyguard. -->
     <dimen name="keyguard_lock_height">42dp</dimen>
@@ -1711,12 +1713,12 @@
     <dimen name="wallet_button_horizontal_padding">24dp</dimen>
     <dimen name="wallet_button_vertical_padding">8dp</dimen>
 
-    <!-- Ongoing call chip -->
-    <dimen name="ongoing_call_chip_side_padding">12dp</dimen>
-    <dimen name="ongoing_call_chip_icon_size">16dp</dimen>
+    <!-- Ongoing activity chip -->
+    <dimen name="ongoing_activity_chip_side_padding">12dp</dimen>
+    <dimen name="ongoing_activity_chip_icon_size">16dp</dimen>
     <!-- The padding between the icon and the text. -->
-    <dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
-    <dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
+    <dimen name="ongoing_activity_chip_icon_text_padding">4dp</dimen>
+    <dimen name="ongoing_activity_chip_corner_radius">28dp</dimen>
 
     <!-- Status bar user chip -->
     <dimen name="status_bar_user_chip_avatar_size">16dp</dimen>
@@ -1961,15 +1963,6 @@
     <dimen name="broadcast_dialog_btn_minHeight">44dp</dimen>
     <dimen name="broadcast_dialog_margin">16dp</dimen>
 
-    <!-- Contrast dialog -->
-    <dimen name="contrast_dialog_button_total_size">90dp</dimen>
-    <dimen name="contrast_dialog_button_inner_size">82dp</dimen>
-    <dimen name="contrast_dialog_button_radius">20dp</dimen>
-    <dimen name="contrast_dialog_button_stroke_width">4dp</dimen>
-    <dimen name="contrast_dialog_button_text_size">14sp</dimen>
-    <dimen name="contrast_dialog_button_text_spacing">4dp</dimen>
-    <dimen name="contrast_dialog_button_horizontal_spacing">16dp</dimen>
-
     <!-- Shadow for dream overlay clock complication -->
     <dimen name="dream_overlay_clock_key_text_shadow_dx">0dp</dimen>
     <dimen name="dream_overlay_clock_key_text_shadow_dy">0dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b5ec5b2..8da8316f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -384,6 +384,8 @@
 
     <!-- Content description for the app logo icon on biometric prompt. [CHAR LIMIT=NONE] -->
     <string name="biometric_dialog_logo">App logo</string>
+    <!-- List of packages for which we want to show overridden logo. For example, an app overrides its launcher logo, if it's in this array, biometric dialog shows the overridden logo; otherwise biometric dialog still shows the default application info icon. [CHAR LIMIT=NONE] -->
+    <string-array name="biometric_dialog_package_names_for_logo_with_overrides" />
     <!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
     <string name="biometric_dialog_confirm">Confirm</string>
     <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR LIMIT=30] -->
@@ -900,15 +902,6 @@
     <!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_onehanded_label">One-handed mode</string>
 
-    <!-- QuickSettings: Contrast tile [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_contrast_label">Contrast</string>
-    <!-- QuickSettings: Contrast tile description: standard [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_contrast_standard">Standard</string>
-    <!-- QuickSettings: Contrast tile description: medium [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_contrast_medium">Medium</string>
-    <!-- QuickSettings: Contrast tile description: high [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_contrast_high">High</string>
-
     <!-- Hearing devices -->
     <!-- QuickSettings: Hearing devices [CHAR LIMIT=NONE] -->
     <string name="quick_settings_hearing_devices_label">Hearing devices</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2c4cdb9..393a1aa 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -523,10 +523,6 @@
         <item name="android:windowExitAnimation">@anim/instant_fade_out</item>
     </style>
 
-    <style name="Theme.SystemUI.ContrastDialog" parent="@android:style/Theme.DeviceDefault.Dialog">
-        <item name="android:windowBackground">@android:color/transparent</item>
-    </style>
-
     <style name="Theme.SystemUI.QuickSettings.Dialog" parent="@style/Theme.SystemUI.Dialog.QuickSettings">
     </style>
 
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt
index 8782962..b99c514 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt
@@ -20,10 +20,11 @@
     object None : PromptKind
 
     data class Biometric(
+        /** The available modalities for the authentication on the prompt. */
         val activeModalities: BiometricModalities = BiometricModalities(),
         // TODO(b/330908557): Use this value to decide whether to show two pane layout, instead of
         // simply depending on rotations.
-        val showTwoPane: Boolean = false
+        val showTwoPane: Boolean = false,
     ) : PromptKind
 
     object Pin : PromptKind
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 8ac1de8..c33b7ce 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -99,7 +99,7 @@
             final float startScale = sourceRectHint.width() <= sourceRectHint.height()
                     ? (float) destinationBounds.width() / sourceBounds.width()
                     : (float) destinationBounds.height() / sourceBounds.height();
-            scale = (1 - progress) * startScale + progress * endScale;
+            scale = Math.min((1 - progress) * startScale + progress * endScale, 1.0f);
         }
         final float left = destinationBounds.left - (insets.left + sourceBounds.left) * scale;
         final float top = destinationBounds.top - (insets.top + sourceBounds.top) * scale;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index d191a3c..d5bc10a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -65,7 +65,7 @@
     /**
      * Sent when some system ui state changes.
      */
-    void onSystemUiStateChanged(int stateFlags) = 16;
+    void onSystemUiStateChanged(long stateFlags) = 16;
 
     /**
      * Sent when suggested rotation button could be shown
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 94b6fd4..090033d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -162,5 +162,10 @@
     oneway void setOverrideHomeButtonLongPress(long duration, float slopMultiplier, boolean haptic)
             = 55;
 
-    // Next id = 56
+    /**
+     * Notifies to toggle quick settings panel.
+     */
+    oneway void toggleQuickSettingsPanel() = 56;
+
+    // Next id = 57
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
deleted file mode 100644
index 0b0df83..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2016 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.shared.recents.model;
-
-import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.graphics.Bitmap.Config.ARGB_8888;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
-import android.util.Log;
-import android.view.WindowInsetsController.Appearance;
-import android.window.TaskSnapshot;
-
-import java.util.HashMap;
-
-/**
- * Data for a single thumbnail.
- */
-public class ThumbnailData {
-
-    public final Bitmap thumbnail;
-    public int orientation;
-    public int rotation;
-    public Rect insets;
-    public Rect letterboxInsets;
-    public boolean reducedResolution;
-    public boolean isRealSnapshot;
-    public boolean isTranslucent;
-    public int windowingMode;
-    public @Appearance int appearance;
-    public float scale;
-    public long snapshotId;
-
-    public ThumbnailData() {
-        thumbnail = null;
-        orientation = ORIENTATION_UNDEFINED;
-        rotation = ROTATION_UNDEFINED;
-        insets = new Rect();
-        letterboxInsets = new Rect();
-        reducedResolution = false;
-        scale = 1f;
-        isRealSnapshot = true;
-        isTranslucent = false;
-        windowingMode = WINDOWING_MODE_UNDEFINED;
-        snapshotId = 0;
-    }
-
-    public void recycleBitmap() {
-        if (thumbnail != null) {
-            thumbnail.recycle();
-        }
-    }
-
-    private static Bitmap makeThumbnail(TaskSnapshot snapshot) {
-        Bitmap thumbnail = null;
-        try (final HardwareBuffer buffer = snapshot.getHardwareBuffer()) {
-            if (buffer != null) {
-                thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());
-            }
-        } catch (IllegalArgumentException ex) {
-            // TODO(b/157562905): Workaround for a crash when we get a snapshot without this state
-            Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: "
-                    + snapshot.getHardwareBuffer(), ex);
-        }
-        if (thumbnail == null) {
-            Point taskSize = snapshot.getTaskSize();
-            thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888);
-            thumbnail.eraseColor(Color.BLACK);
-        }
-        return thumbnail;
-    }
-
-    public static HashMap<Integer, ThumbnailData> wrap(int[] taskIds, TaskSnapshot[] snapshots) {
-        HashMap<Integer, ThumbnailData> temp = new HashMap<>();
-        if (taskIds == null || snapshots == null || taskIds.length != snapshots.length) {
-            return temp;
-        }
-
-        for (int i = snapshots.length - 1; i >= 0; i--) {
-            temp.put(taskIds[i], new ThumbnailData(snapshots[i]));
-        }
-        return temp;
-    }
-
-    public ThumbnailData(TaskSnapshot snapshot) {
-        thumbnail = makeThumbnail(snapshot);
-        insets = new Rect(snapshot.getContentInsets());
-        letterboxInsets = new Rect(snapshot.getLetterboxInsets());
-        orientation = snapshot.getOrientation();
-        rotation = snapshot.getRotation();
-        reducedResolution = snapshot.isLowResolution();
-        // TODO(b/149579527): Pass task size instead of computing scale.
-        // Assume width and height were scaled the same; compute scale only for width
-        scale = (float) thumbnail.getWidth() / snapshot.getTaskSize().x;
-        isRealSnapshot = snapshot.isRealSnapshot();
-        isTranslucent = snapshot.isTranslucent();
-        windowingMode = snapshot.getWindowingMode();
-        appearance = snapshot.getAppearance();
-        snapshotId = snapshot.getId();
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt
new file mode 100644
index 0000000..dcf7754
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 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.shared.recents.model
+
+import android.app.WindowConfiguration
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Bitmap.Config.ARGB_8888
+import android.graphics.Color
+import android.graphics.Rect
+import android.util.Log
+import android.view.WindowInsetsController.Appearance
+import android.window.TaskSnapshot
+
+/** Data for a single thumbnail. */
+data class ThumbnailData(
+    val thumbnail: Bitmap? = null,
+    var orientation: Int = Configuration.ORIENTATION_UNDEFINED,
+    @JvmField var rotation: Int = WindowConfiguration.ROTATION_UNDEFINED,
+    @JvmField var insets: Rect = Rect(),
+    @JvmField var letterboxInsets: Rect = Rect(),
+    @JvmField var reducedResolution: Boolean = false,
+    @JvmField var isRealSnapshot: Boolean = true,
+    var isTranslucent: Boolean = false,
+    @JvmField var windowingMode: Int = WindowConfiguration.WINDOWING_MODE_UNDEFINED,
+    @JvmField @Appearance var appearance: Int = 0,
+    @JvmField var scale: Float = 1f,
+    var snapshotId: Long = 0,
+) {
+    fun recycleBitmap() {
+        thumbnail?.recycle()
+    }
+
+    companion object {
+        private fun makeThumbnail(snapshot: TaskSnapshot): Bitmap {
+            var thumbnail: Bitmap? = null
+            try {
+                snapshot.hardwareBuffer?.use { buffer ->
+                    thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.colorSpace)
+                }
+            } catch (ex: IllegalArgumentException) {
+                // TODO(b/157562905): Workaround for a crash when we get a snapshot without this
+                // state
+                Log.e(
+                    "ThumbnailData",
+                    "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: " +
+                        "${snapshot.hardwareBuffer}",
+                    ex
+                )
+            }
+
+            return thumbnail
+                ?: Bitmap.createBitmap(snapshot.taskSize.x, snapshot.taskSize.y, ARGB_8888).apply {
+                    eraseColor(Color.BLACK)
+                }
+        }
+
+        @JvmStatic
+        fun wrap(taskIds: IntArray?, snapshots: Array<TaskSnapshot>?): HashMap<Int, ThumbnailData> {
+            return if (taskIds == null || snapshots == null || taskIds.size != snapshots.size) {
+                HashMap()
+            } else {
+                HashMap(taskIds.associateWith { taskId -> fromSnapshot(snapshots[taskId]) })
+            }
+        }
+
+        @JvmStatic
+        fun fromSnapshot(snapshot: TaskSnapshot): ThumbnailData {
+            val thumbnail = makeThumbnail(snapshot)
+            return ThumbnailData(
+                thumbnail = thumbnail,
+                insets = Rect(snapshot.contentInsets),
+                letterboxInsets = Rect(snapshot.letterboxInsets),
+                orientation = snapshot.orientation,
+                rotation = snapshot.rotation,
+                reducedResolution = snapshot.isLowResolution,
+                // TODO(b/149579527): Pass task size instead of computing scale.
+                // Assume width and height were scaled the same; compute scale only for width
+                scale = thumbnail.width.toFloat() / snapshot.taskSize.x,
+                isRealSnapshot = snapshot.isRealSnapshot,
+                isTranslucent = snapshot.isTranslucent,
+                windowingMode = snapshot.windowingMode,
+                appearance = snapshot.appearance,
+                snapshotId = snapshot.id,
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index ca63483..845ca5e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -147,7 +147,7 @@
             Log.w(TAG, "Failed to retrieve task snapshot", e);
         }
         if (snapshot != null) {
-            return new ThumbnailData(snapshot);
+            return ThumbnailData.fromSnapshot(snapshot);
         } else {
             return new ThumbnailData();
         }
@@ -167,7 +167,7 @@
             Log.w(TAG, "Failed to take task snapshot", e);
         }
         if (snapshot != null) {
-            return new ThumbnailData(snapshot);
+            return ThumbnailData.fromSnapshot(snapshot);
         } else {
             return new ThumbnailData();
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 69aa909..b4377ea 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -22,7 +22,7 @@
 
 import static com.android.systemui.shared.Flags.shadeAllowBackGesture;
 
-import android.annotation.IntDef;
+import android.annotation.LongDef;
 import android.content.Context;
 import android.content.res.Resources;
 import android.view.ViewConfiguration;
@@ -51,92 +51,94 @@
 
     // Overview is disabled, either because the device is in lock task mode, or because the device
     // policy has disabled the feature
-    public static final int SYSUI_STATE_SCREEN_PINNING = 1 << 0;
+    public static final long SYSUI_STATE_SCREEN_PINNING = 1L << 0;
     // The navigation bar is hidden due to immersive mode
-    public static final int SYSUI_STATE_NAV_BAR_HIDDEN = 1 << 1;
+    public static final long SYSUI_STATE_NAV_BAR_HIDDEN = 1L << 1;
     // The notification panel is expanded and interactive (either locked or unlocked), and the
     // quick settings is not expanded
-    public static final int SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED = 1 << 2;
+    public static final long SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED = 1L << 2;
     // The keyguard bouncer is showing
-    public static final int SYSUI_STATE_BOUNCER_SHOWING = 1 << 3;
+    public static final long SYSUI_STATE_BOUNCER_SHOWING = 1L << 3;
     // The navigation bar a11y button should be shown
-    public static final int SYSUI_STATE_A11Y_BUTTON_CLICKABLE = 1 << 4;
+    public static final long SYSUI_STATE_A11Y_BUTTON_CLICKABLE = 1L << 4;
     // The navigation bar a11y button shortcut is available
-    public static final int SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE = 1 << 5;
+    public static final long SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE = 1L << 5;
     // The keyguard is showing and not occluded
-    public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING = 1 << 6;
+    public static final long SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING = 1L << 6;
     // The recents feature is disabled (either by SUW/SysUI/device policy)
-    public static final int SYSUI_STATE_OVERVIEW_DISABLED = 1 << 7;
+    public static final long SYSUI_STATE_OVERVIEW_DISABLED = 1L << 7;
     // The home feature is disabled (either by SUW/SysUI/device policy)
-    public static final int SYSUI_STATE_HOME_DISABLED = 1 << 8;
+    public static final long SYSUI_STATE_HOME_DISABLED = 1L << 8;
     // The keyguard is showing, but occluded
-    public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED = 1 << 9;
+    public static final long SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED = 1L << 9;
     // The search feature is disabled (either by SUW/SysUI/device policy)
-    public static final int SYSUI_STATE_SEARCH_DISABLED = 1 << 10;
+    public static final long SYSUI_STATE_SEARCH_DISABLED = 1L << 10;
     // The notification panel is expanded and interactive (either locked or unlocked), and quick
     // settings is expanded.
-    public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
+    public static final long SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1L << 11;
     // Winscope tracing is enabled
-    public static final int SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION = 1 << 12;
+    public static final long SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION = 1L << 12;
     // The Assistant gesture should be constrained. It is up to the launcher implementation to
     // decide how to constrain it
-    public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
+    public static final long SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1L << 13;
     // The bubble stack is expanded. This means that the home gesture should be ignored, since a
     // swipe up is an attempt to close the bubble stack, but that the back gesture should remain
     // enabled (since it's used to navigate back within the bubbled app, or to collapse the bubble
     // stack.
-    public static final int SYSUI_STATE_BUBBLES_EXPANDED = 1 << 14;
+    public static final long SYSUI_STATE_BUBBLES_EXPANDED = 1L << 14;
     // A SysUI dialog is showing.
-    public static final int SYSUI_STATE_DIALOG_SHOWING = 1 << 15;
+    public static final long SYSUI_STATE_DIALOG_SHOWING = 1L << 15;
     // The one-handed mode is active
-    public static final int SYSUI_STATE_ONE_HANDED_ACTIVE = 1 << 16;
+    public static final long SYSUI_STATE_ONE_HANDED_ACTIVE = 1L << 16;
     // Allow system gesture no matter the system bar(s) is visible or not
-    public static final int SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1 << 17;
+    public static final long SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1L << 17;
     // The IME is showing
-    public static final int SYSUI_STATE_IME_SHOWING = 1 << 18;
+    public static final long SYSUI_STATE_IME_SHOWING = 1L << 18;
     // The window magnification is overlapped with system gesture insets at the bottom.
-    public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19;
+    public static final long SYSUI_STATE_MAGNIFICATION_OVERLAP = 1L << 19;
     // ImeSwitcher is showing
-    public static final int SYSUI_STATE_IME_SWITCHER_SHOWING = 1 << 20;
+    public static final long SYSUI_STATE_IME_SWITCHER_SHOWING = 1L << 20;
     // Device dozing/AOD state
-    public static final int SYSUI_STATE_DEVICE_DOZING = 1 << 21;
+    public static final long SYSUI_STATE_DEVICE_DOZING = 1L << 21;
     // The home feature is disabled (either by SUW/SysUI/device policy)
-    public static final int SYSUI_STATE_BACK_DISABLED = 1 << 22;
+    public static final long SYSUI_STATE_BACK_DISABLED = 1L << 22;
     // The bubble stack is expanded AND the mange menu for bubbles is expanded on top of it.
-    public static final int SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED = 1 << 23;
+    public static final long SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED = 1L << 23;
     // The voice interaction session window is showing
-    public static final int SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 25;
+    public static final long SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING = 1L << 25;
     // Freeform windows are showing in desktop mode
-    public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26;
+    public static final long SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1L << 26;
     // Device dreaming state
-    public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27;
+    public static final long SYSUI_STATE_DEVICE_DREAMING = 1L << 27;
     // Whether the device is currently awake (as opposed to asleep, see WakefulnessLifecycle).
     // Note that the device is awake on while waking up on, but not while going to sleep.
-    public static final int SYSUI_STATE_AWAKE = 1 << 28;
+    public static final long SYSUI_STATE_AWAKE = 1L << 28;
     // Whether the device is currently transitioning between awake/asleep indicated by
     // SYSUI_STATE_AWAKE.
-    public static final int SYSUI_STATE_WAKEFULNESS_TRANSITION = 1 << 29;
+    public static final long SYSUI_STATE_WAKEFULNESS_TRANSITION = 1L << 29;
     // The notification panel expansion fraction is > 0
-    public static final int SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE = 1 << 30;
+    public static final long SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE = 1L << 30;
     // When keyguard will be dismissed but didn't start animation yet
-    public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY = 1 << 31;
+    public static final long SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY = 1L << 31;
+    // Physical keyboard shortcuts helper is showing
+    public static final long SYSUI_STATE_SHORTCUT_HELPER_SHOWING = 1L << 32;
 
     // Mask for SystemUiStateFlags to isolate SYSUI_STATE_AWAKE and
     // SYSUI_STATE_WAKEFULNESS_TRANSITION, to match WAKEFULNESS_* constants
-    public static final int SYSUI_STATE_WAKEFULNESS_MASK =
+    public static final long SYSUI_STATE_WAKEFULNESS_MASK =
             SYSUI_STATE_AWAKE | SYSUI_STATE_WAKEFULNESS_TRANSITION;
     // Mirroring the WakefulnessLifecycle#Wakefulness states
-    public static final int WAKEFULNESS_ASLEEP = 0;
-    public static final int WAKEFULNESS_AWAKE = SYSUI_STATE_AWAKE;
-    public static final int WAKEFULNESS_GOING_TO_SLEEP = SYSUI_STATE_WAKEFULNESS_TRANSITION;
-    public static final int WAKEFULNESS_WAKING =
+    public static final long WAKEFULNESS_ASLEEP = 0;
+    public static final long WAKEFULNESS_AWAKE = SYSUI_STATE_AWAKE;
+    public static final long WAKEFULNESS_GOING_TO_SLEEP = SYSUI_STATE_WAKEFULNESS_TRANSITION;
+    public static final long WAKEFULNESS_WAKING =
             SYSUI_STATE_WAKEFULNESS_TRANSITION | SYSUI_STATE_AWAKE;
 
     // Whether the back gesture is allowed (or ignored) by the Shade
     public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = shadeAllowBackGesture();
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SYSUI_STATE_SCREEN_PINNING,
+    @LongDef({SYSUI_STATE_SCREEN_PINNING,
             SYSUI_STATE_NAV_BAR_HIDDEN,
             SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
             SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
@@ -167,10 +169,11 @@
             SYSUI_STATE_WAKEFULNESS_TRANSITION,
             SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE,
             SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY,
+            SYSUI_STATE_SHORTCUT_HELPER_SHOWING,
     })
     public @interface SystemUiStateFlags {}
 
-    public static String getSystemUiStateString(int flags) {
+    public static String getSystemUiStateString(long flags) {
         StringJoiner str = new StringJoiner("|");
         if ((flags & SYSUI_STATE_SCREEN_PINNING) != 0) {
             str.add("screen_pinned");
@@ -265,6 +268,9 @@
         if ((flags & SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY) != 0) {
             str.add("keygrd_going_away");
         }
+        if ((flags & SYSUI_STATE_SHORTCUT_HELPER_SHOWING) != 0) {
+            str.add("shortcut_helper_showing");
+        }
 
         return str.toString();
     }
@@ -285,13 +291,13 @@
      * Returns whether the specified sysui state is such that the assistant gesture should be
      * disabled.
      */
-    public static boolean isAssistantGestureDisabled(int sysuiStateFlags) {
+    public static boolean isAssistantGestureDisabled(long sysuiStateFlags) {
         if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
             sysuiStateFlags &= ~SYSUI_STATE_NAV_BAR_HIDDEN;
         }
         // Disable when in quick settings, screen pinning, immersive, the bouncer is showing, 
         // or search is disabled
-        int disableFlags = SYSUI_STATE_SCREEN_PINNING
+        long disableFlags = SYSUI_STATE_SCREEN_PINNING
                 | SYSUI_STATE_NAV_BAR_HIDDEN
                 | SYSUI_STATE_BOUNCER_SHOWING
                 | SYSUI_STATE_SEARCH_DISABLED
@@ -313,7 +319,7 @@
      * Returns whether the specified sysui state is such that the back gesture should be
      * disabled.
      */
-    public static boolean isBackGestureDisabled(int sysuiStateFlags, boolean forTrackpad) {
+    public static boolean isBackGestureDisabled(long sysuiStateFlags, boolean forTrackpad) {
         // Always allow when the bouncer/global actions/voice session is showing (even on top of
         // the keyguard)
         if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0
@@ -328,9 +334,9 @@
         return (sysuiStateFlags & getBackGestureDisabledMask(forTrackpad)) != 0;
     }
 
-    private static int getBackGestureDisabledMask(boolean forTrackpad) {
+    private static long getBackGestureDisabledMask(boolean forTrackpad) {
         // Disable when in immersive, or the notifications are interactive
-        int disableFlags = SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+        long disableFlags = SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
         if (!forTrackpad) {
             disableFlags |= SYSUI_STATE_NAV_BAR_HIDDEN;
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index a6e04ce..bbf4698 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -42,7 +42,7 @@
         try {
             final TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
             if (snapshot != null) {
-                return new ThumbnailData(snapshot);
+                return ThumbnailData.fromSnapshot(snapshot);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to screenshot task", e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 473719fa..cf8ec62 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -351,7 +351,7 @@
                     case ON_TASK_SNAPSHOT_CHANGED: {
                         Trace.beginSection("onTaskSnapshotChanged");
                         final TaskSnapshot snapshot = (TaskSnapshot) msg.obj;
-                        final ThumbnailData thumbnail = new ThumbnailData(snapshot);
+                        final ThumbnailData thumbnail = ThumbnailData.fromSnapshot(snapshot);
                         boolean snapshotConsumed = false;
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                             boolean consumed = mTaskStackListeners.get(i).onTaskSnapshotChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index f33acf2..3f3bb0b 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
@@ -544,10 +545,10 @@
     internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
         return scope.launch {
             merge(
-                    keyguardTransitionInteractor.transition(AOD, LOCKSCREEN).map { step ->
-                        step.copy(value = 1f - step.value)
+                    keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN)).map {
+                        it.copy(value = 1f - it.value)
                     },
-                    keyguardTransitionInteractor.transition(LOCKSCREEN, AOD),
+                    keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD)),
                 ).filter {
                     it.transitionState != TransitionState.FINISHED
                 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 905a98c..42838ae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -326,7 +326,8 @@
             }
 
             if (KeyguardWmStateRefactor.isEnabled()) {
-                mKeyguardTransitionInteractor.startDismissKeyguardTransition();
+                mKeyguardTransitionInteractor.startDismissKeyguardTransition(
+                        "KeyguardSecurityContainerController#finish");
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index 1f04599..d5e911e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -37,7 +37,6 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Flags;
 
 import java.util.HashMap;
 
@@ -339,15 +338,11 @@
         mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ true);
         final PointF position = mMenuView.getMenuPosition();
         final PointF tuckedPosition = getTuckedMenuPosition();
-        if (Flags.floatingMenuAnimatedTuck()) {
-            flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_X,
-                    Math.signum(tuckedPosition.x - position.x) * ESCAPE_VELOCITY,
-                    FLING_FRICTION_SCALAR,
-                    createDefaultSpringForce(),
-                    tuckedPosition.x);
-        } else {
-            moveToPosition(tuckedPosition);
-        }
+        flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_X,
+                Math.signum(tuckedPosition.x - position.x) * ESCAPE_VELOCITY,
+                FLING_FRICTION_SCALAR,
+                createDefaultSpringForce(),
+                tuckedPosition.x);
 
         // Keep the touch region let users could click extra space to pop up the menu view
         // from the screen edge
@@ -359,23 +354,19 @@
     void moveOutEdgeAndShow() {
         mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ false);
 
-        if (Flags.floatingMenuAnimatedTuck()) {
-            PointF position = mMenuView.getMenuPosition();
-            springMenuWith(DynamicAnimation.TRANSLATION_X,
-                    createDefaultSpringForce(),
-                    0,
-                    position.x,
-                    true
-            );
-            springMenuWith(DynamicAnimation.TRANSLATION_Y,
-                    createDefaultSpringForce(),
-                    0,
-                    position.y,
-                    true
-            );
-        } else {
-            mMenuView.onPositionChanged();
-        }
+        PointF position = mMenuView.getMenuPosition();
+        springMenuWith(DynamicAnimation.TRANSLATION_X,
+                createDefaultSpringForce(),
+                0,
+                position.x,
+                true
+        );
+        springMenuWith(DynamicAnimation.TRANSLATION_Y,
+                createDefaultSpringForce(),
+                0,
+                position.y,
+                true
+        );
 
         mMenuView.onEdgeChangedIfNeeded();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index be75e10..9d9e7df 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -321,22 +321,6 @@
         if (mMoveToTuckedListener != null) {
             mMoveToTuckedListener.onMoveToTuckedChanged(isMoveToTucked);
         }
-
-        if (!Flags.floatingMenuAnimatedTuck()) {
-            if (isMoveToTucked) {
-                final float halfWidth = getMenuWidth() / 2.0f;
-                final boolean isOnLeftSide = mMenuAnimationController.isOnLeftSide();
-                final Rect clipBounds = new Rect(
-                        (int) (!isOnLeftSide ? 0 : halfWidth),
-                        0,
-                        (int) (!isOnLeftSide ? halfWidth : getMenuWidth()),
-                        getMenuHeight()
-                );
-                setClipBounds(clipBounds);
-            } else {
-                setClipBounds(null);
-            }
-        }
     }
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 6dce1bb..0c67c50 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -322,9 +322,8 @@
         }
         addView(mMessageView, LayerIndex.MESSAGE_VIEW);
 
-        if (Flags.floatingMenuAnimatedTuck()) {
-            setClipChildren(true);
-        }
+        setClipChildren(true);
+
         setClickable(false);
         setFocusable(false);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
@@ -476,10 +475,8 @@
             mMenuAnimationController.startTuckedAnimationPreview();
         }
 
-        if (Flags.floatingMenuAnimatedTuck()) {
-            if (!mMenuView.isMoveToTucked()) {
-                setClipBounds(null);
-            }
+        if (!mMenuView.isMoveToTucked()) {
+            setClipBounds(null);
         }
         mMenuView.onArrivalAtPosition(false);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
index 9c7fc9d..9ef9938 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
@@ -23,8 +23,7 @@
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 
-import androidx.annotation.NonNull;
-
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
 import java.util.Optional;
@@ -38,34 +37,29 @@
  */
 public class ShadeTouchHandler implements TouchHandler {
     private final Optional<CentralSurfaces> mSurfaces;
+    private final ShadeViewController mShadeViewController;
     private final int mInitiationHeight;
 
-    /**
-     * Tracks whether or not we are capturing a given touch. Will be null before and after a touch.
-     */
-    private Boolean mCapture;
-
     @Inject
     ShadeTouchHandler(Optional<CentralSurfaces> centralSurfaces,
+            ShadeViewController shadeViewController,
             @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) int initiationHeight) {
         mSurfaces = centralSurfaces;
+        mShadeViewController = shadeViewController;
         mInitiationHeight = initiationHeight;
     }
 
     @Override
     public void onSessionStart(TouchSession session) {
-        if (mSurfaces.isEmpty()) {
+        if (mSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) {
             session.pop();
             return;
         }
 
-        session.registerCallback(() -> mCapture = null);
-
         session.registerInputListener(ev -> {
+            mShadeViewController.handleExternalTouch((MotionEvent) ev);
+
             if (ev instanceof MotionEvent) {
-                if (mCapture != null && mCapture) {
-                    mSurfaces.get().handleExternalShadeWindowTouch((MotionEvent) ev);
-                }
                 if (((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
                     session.pop();
                 }
@@ -74,25 +68,15 @@
 
         session.registerGestureListener(new GestureDetector.SimpleOnGestureListener() {
             @Override
-            public boolean onScroll(MotionEvent e1, @NonNull MotionEvent e2, float distanceX,
+            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
                     float distanceY) {
-                if (mCapture == null) {
-                    // Only capture swipes that are going downwards.
-                    mCapture = Math.abs(distanceY) > Math.abs(distanceX) && distanceY < 0;
-                    if (mCapture) {
-                        // Send the initial touches over, as the input listener has already
-                        // processed these touches.
-                        mSurfaces.get().handleExternalShadeWindowTouch(e1);
-                        mSurfaces.get().handleExternalShadeWindowTouch(e2);
-                    }
-                }
-                return mCapture;
+                return true;
             }
 
             @Override
-            public boolean onFling(MotionEvent e1, @NonNull MotionEvent e2, float velocityX,
+            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                     float velocityY) {
-                return mCapture;
+                return true;
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 9ba41ef..298c0f7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -73,7 +73,6 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.biometrics.AuthController.ScaleFactorProvider;
-import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
 import com.android.systemui.biometrics.shared.model.BiometricModalities;
 import com.android.systemui.biometrics.shared.model.PromptKind;
@@ -150,7 +149,6 @@
     private final CoroutineScope mApplicationCoroutineScope;
 
     // TODO(b/287311775): these should be migrated out once ready
-    private final Provider<PromptCredentialInteractor> mPromptCredentialInteractor;
     private final @NonNull Provider<PromptSelectorInteractor> mPromptSelectorInteractorProvider;
     // TODO(b/287311775): these should be migrated out of the view
     private final Provider<CredentialViewModel> mCredentialViewModelProvider;
@@ -311,7 +309,6 @@
             @NonNull UserManager userManager,
             @NonNull LockPatternUtils lockPatternUtils,
             @NonNull InteractionJankMonitor jankMonitor,
-            @NonNull Provider<PromptCredentialInteractor> promptCredentialInteractor,
             @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractor,
             @NonNull PromptViewModel promptViewModel,
             @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@@ -319,7 +316,7 @@
             @NonNull VibratorHelper vibratorHelper) {
         this(config, applicationCoroutineScope, fpProps, faceProps,
                 wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
-                jankMonitor, promptSelectorInteractor, promptCredentialInteractor, promptViewModel,
+                jankMonitor, promptSelectorInteractor, promptViewModel,
                 credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor,
                 vibratorHelper);
     }
@@ -335,7 +332,6 @@
             @NonNull LockPatternUtils lockPatternUtils,
             @NonNull InteractionJankMonitor jankMonitor,
             @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider,
-            @NonNull Provider<PromptCredentialInteractor> credentialInteractor,
             @NonNull PromptViewModel promptViewModel,
             @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
             @NonNull Handler mainHandler,
@@ -358,13 +354,19 @@
         mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
         mBiometricCallback = new BiometricCallback();
 
+        mFpProps = fpProps;
+        mFaceProps = faceProps;
+        final BiometricModalities biometricModalities = new BiometricModalities(
+                Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
+                Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds));
+
         mPromptSelectorInteractorProvider = promptSelectorInteractorProvider;
-        mPromptSelectorInteractorProvider.get().setShouldShowBpWithoutIconForCredential(
-                config.mPromptInfo);
+        mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mEffectiveUserId,
+                biometricModalities, mConfig.mOperationId, mConfig.mOpPackageName,
+                false /*onSwitchToCredential*/);
 
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
-        if (constraintBp() && (Utils.isBiometricAllowed(config.mPromptInfo)
-                || mPromptViewModel.getShowBpWithoutIconForCredential().getValue())) {
+        if (constraintBp() && mPromptViewModel.getPromptKind().getValue().isBiometric()) {
             mLayout = (ConstraintLayout) layoutInflater.inflate(
                     R.layout.biometric_prompt_constraint_layout, this, false /* attachToRoot */);
         } else {
@@ -398,10 +400,7 @@
         mPanelController = new AuthPanelController(mContext, mPanelView);
         mBackgroundExecutor = bgExecutor;
         mInteractionJankMonitor = jankMonitor;
-        mPromptCredentialInteractor = credentialInteractor;
         mCredentialViewModelProvider = credentialViewModelProvider;
-        mFpProps = fpProps;
-        mFaceProps = faceProps;
 
         showPrompt(config, layoutInflater, promptViewModel,
                 Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
@@ -430,11 +429,12 @@
             @Nullable FaceSensorPropertiesInternal faceProps,
             @NonNull VibratorHelper vibratorHelper
     ) {
-        if (Utils.isBiometricAllowed(config.mPromptInfo)
-                || mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
+        if (mPromptViewModel.getPromptKind().getValue().isBiometric()) {
             addBiometricView(config, layoutInflater, viewModel, fpProps, faceProps, vibratorHelper);
-        } else if (constraintBp() && Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
-            addCredentialView(true, false);
+        } else if (mPromptViewModel.getPromptKind().getValue().isCredential()) {
+            if (constraintBp()) {
+                addCredentialView(true, false);
+            }
         } else {
             mPromptSelectorInteractorProvider.get().resetPrompt();
         }
@@ -445,12 +445,6 @@
             @Nullable FingerprintSensorPropertiesInternal fpProps,
             @Nullable FaceSensorPropertiesInternal faceProps,
             @NonNull VibratorHelper vibratorHelper) {
-        mPromptSelectorInteractorProvider.get().useBiometricsForAuthentication(
-                config.mPromptInfo,
-                config.mUserId,
-                config.mOperationId,
-                new BiometricModalities(fpProps, faceProps),
-                config.mOpPackageName);
 
         if (constraintBp()) {
             mBiometricView = BiometricViewBinder.bind(mLayout, viewModel, null,
@@ -519,10 +513,6 @@
         // disable it.
         mBackgroundView.setOnClickListener(null);
         mBackgroundView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
-
-        mPromptSelectorInteractorProvider.get().useCredentialsForAuthentication(
-                mConfig.mPromptInfo, credentialType, mConfig.mUserId, mConfig.mOperationId,
-                mConfig.mOpPackageName);
         final CredentialViewModel vm = mCredentialViewModelProvider.get();
         vm.setAnimateContents(animateContents);
         ((CredentialView) mCredentialView).init(vm, this, mPanelController, animatePanel,
@@ -557,10 +547,9 @@
                 () -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
         if (constraintBp()) {
             // Do nothing on attachment with constraintLayout
-        } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo)
-                || mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
+        } else if (mPromptViewModel.getPromptKind().getValue().isBiometric()) {
             mBiometricScrollView.addView(mBiometricView.asView());
-        } else if (Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
+        } else if (mPromptViewModel.getPromptKind().getValue().isCredential()) {
             addCredentialView(true /* animatePanel */, false /* animateContents */);
         } else {
             throw new IllegalStateException("Unknown configuration: "
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index ca88d40d..d6d40f2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -69,7 +69,6 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
-import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
@@ -139,7 +138,6 @@
     private Job mBiometricContextListenerJob = null;
 
     // TODO: these should be migrated out once ready
-    @NonNull private final Provider<PromptCredentialInteractor> mPromptCredentialInteractor;
     @NonNull private final Provider<PromptSelectorInteractor> mPromptSelectorInteractor;
     @NonNull private final Provider<CredentialViewModel> mCredentialViewModelProvider;
     @NonNull private final Provider<PromptViewModel> mPromptViewModelProvider;
@@ -735,7 +733,6 @@
             @NonNull LockPatternUtils lockPatternUtils,
             @NonNull Lazy<UdfpsLogger> udfpsLogger,
             @NonNull Lazy<LogContextInteractor> logContextInteractor,
-            @NonNull Provider<PromptCredentialInteractor> promptCredentialInteractorProvider,
             @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider,
             @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
             @NonNull Provider<PromptViewModel> promptViewModelProvider,
@@ -768,7 +765,6 @@
 
         mLogContextInteractor = logContextInteractor;
         mPromptSelectorInteractor = promptSelectorInteractorProvider;
-        mPromptCredentialInteractor = promptCredentialInteractorProvider;
         mPromptViewModelProvider = promptViewModelProvider;
         mCredentialViewModelProvider = credentialViewModelProvider;
 
@@ -1253,6 +1249,8 @@
         }
         mCurrentDialog = newDialog;
 
+        // TODO(b/339532378): We should check whether |allowBackgroundAuthentication| should be
+        //  removed.
         if (!promptInfo.isAllowBackgroundAuthentication() && !isOwnerInForeground()) {
             cancelIfOwnerIsNotInForeground();
         } else {
@@ -1316,8 +1314,8 @@
         config.mScaleProvider = this::getScaleFactor;
         return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps,
                 wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
-                mInteractionJankMonitor, mPromptCredentialInteractor, mPromptSelectorInteractor,
-                viewModel, mCredentialViewModelProvider, bgExecutor, mVibratorHelper);
+                mInteractionJankMonitor, mPromptSelectorInteractor, viewModel,
+                mCredentialViewModelProvider, bgExecutor, mVibratorHelper);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 9816896..298b87d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -32,11 +32,18 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.StatusBarState
@@ -131,6 +138,7 @@
             override fun onUnlockedChanged() {
                 updatePauseAuth()
             }
+
             override fun onLaunchTransitionFadingAwayChanged() {
                 launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
                 updatePauseAuth()
@@ -211,7 +219,10 @@
     suspend fun listenForPrimaryBouncerToAodTransitions(scope: CoroutineScope): Job {
         return scope.launch {
             transitionInteractor
-                .transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD)
+                .transition(
+                    edge = Edge.create(Scenes.Bouncer, AOD),
+                    edgeWithoutSceneContainer = Edge.create(PRIMARY_BOUNCER, AOD)
+                )
                 .collect { transitionStep ->
                     view.onDozeAmountChanged(
                         transitionStep.value,
@@ -225,8 +236,7 @@
     @VisibleForTesting
     suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job {
         return scope.launch {
-            transitionInteractor.transition(KeyguardState.DREAMING, KeyguardState.AOD).collect {
-                transitionStep ->
+            transitionInteractor.transition(Edge.create(DREAMING, AOD)).collect { transitionStep ->
                 view.onDozeAmountChanged(
                     transitionStep.value,
                     transitionStep.value,
@@ -239,23 +249,21 @@
     @VisibleForTesting
     suspend fun listenForAlternateBouncerToAodTransitions(scope: CoroutineScope): Job {
         return scope.launch {
-            transitionInteractor
-                .transition(KeyguardState.ALTERNATE_BOUNCER, KeyguardState.AOD)
-                .collect { transitionStep ->
-                    view.onDozeAmountChanged(
-                        transitionStep.value,
-                        transitionStep.value,
-                        UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
-                    )
-                }
+            transitionInteractor.transition(Edge.create(ALTERNATE_BOUNCER, AOD)).collect {
+                transitionStep ->
+                view.onDozeAmountChanged(
+                    transitionStep.value,
+                    transitionStep.value,
+                    UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
+                )
+            }
         }
     }
 
     @VisibleForTesting
     suspend fun listenForAodToOccludedTransitions(scope: CoroutineScope): Job {
         return scope.launch {
-            transitionInteractor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED).collect {
-                transitionStep ->
+            transitionInteractor.transition(Edge.create(AOD, OCCLUDED)).collect { transitionStep ->
                 view.onDozeAmountChanged(
                     1f - transitionStep.value,
                     1f - transitionStep.value,
@@ -268,8 +276,7 @@
     @VisibleForTesting
     suspend fun listenForOccludedToAodTransition(scope: CoroutineScope): Job {
         return scope.launch {
-            transitionInteractor.transition(KeyguardState.OCCLUDED, KeyguardState.AOD).collect {
-                transitionStep ->
+            transitionInteractor.transition(Edge.create(OCCLUDED, AOD)).collect { transitionStep ->
                 view.onDozeAmountChanged(
                     transitionStep.value,
                     transitionStep.value,
@@ -282,14 +289,18 @@
     @VisibleForTesting
     suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job {
         return scope.launch {
-            transitionInteractor.transition(KeyguardState.GONE, KeyguardState.AOD).collect {
-                transitionStep ->
-                view.onDozeAmountChanged(
-                    transitionStep.value,
-                    transitionStep.value,
-                    ANIMATE_APPEAR_ON_SCREEN_OFF,
+            transitionInteractor
+                .transition(
+                    edge = Edge.create(Scenes.Gone, AOD),
+                    edgeWithoutSceneContainer = Edge.create(GONE, AOD)
                 )
-            }
+                .collect { transitionStep ->
+                    view.onDozeAmountChanged(
+                        transitionStep.value,
+                        transitionStep.value,
+                        ANIMATE_APPEAR_ON_SCREEN_OFF,
+                    )
+                }
         }
     }
 
@@ -298,13 +309,10 @@
         return scope.launch {
             transitionInteractor.dozeAmountTransition.collect { transitionStep ->
                 if (
-                    transitionStep.from == KeyguardState.AOD &&
+                    transitionStep.from == AOD &&
                         transitionStep.transitionState == TransitionState.CANCELED
                 ) {
-                    if (
-                        transitionInteractor.startedKeyguardTransitionStep.first().to !=
-                            KeyguardState.AOD
-                    ) {
+                    if (transitionInteractor.startedKeyguardTransitionStep.first().to != AOD) {
                         // If the next started transition isn't transitioning back to AOD, force
                         // doze amount to be 0f (as if the transition to the lockscreen completed).
                         view.onDozeAmountChanged(
@@ -557,6 +565,7 @@
     private fun updateScaleFactor() {
         udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) }
     }
+
     companion object {
         const val TAG = "UdfpsKeyguardViewController"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 20e81c2..14d8caf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -16,8 +16,10 @@
 
 package com.android.systemui.biometrics.dagger
 
+import android.content.Context
 import android.content.res.Resources
 import com.android.internal.R
+import com.android.launcher3.icons.IconProvider
 import com.android.systemui.CoreStartable
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.EllipseOverlapDetectorParams
@@ -111,6 +113,9 @@
         @Provides fun providesUdfpsUtils(): UdfpsUtils = UdfpsUtils()
 
         @Provides
+        fun provideIconProvider(context: Context): IconProvider = IconProvider(context)
+
+        @Provides
         @SysUISingleton
         fun providesOverlapDetector(): OverlapDetector {
             val selectedOption =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
index cc52484..ca03a00 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
@@ -34,6 +34,7 @@
 import android.hardware.biometrics.events.AuthenticationSucceededInfo
 import android.hardware.face.FaceManager
 import android.hardware.fingerprint.FingerprintManager
+import android.util.Log
 import com.android.systemui.biometrics.shared.model.AuthenticationReason
 import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
 import com.android.systemui.biometrics.shared.model.AuthenticationState
@@ -52,6 +53,7 @@
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.shareIn
 
 /** A repository for the state of biometric authentication. */
@@ -85,6 +87,7 @@
     private val authenticationState: Flow<AuthenticationState> =
         conflatedCallbackFlow {
                 val updateAuthenticationState = { state: AuthenticationState ->
+                    Log.d(TAG, "authenticationState updated: $state")
                     trySendWithFailureLogging(state, TAG, "Error sending AuthenticationState state")
                 }
 
@@ -187,6 +190,7 @@
                         it.biometricSourceType == BiometricSourceType.FINGERPRINT)
             }
             .map { it.requestReason }
+            .onEach { Log.d(TAG, "fingerprintAuthenticationReason updated: $it") }
 
     override val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus> =
         authenticationState
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
index 40d38dd..6b61adc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
@@ -30,10 +30,10 @@
 import com.android.systemui.biometrics.shared.model.toSensorStrength
 import com.android.systemui.biometrics.shared.model.toSensorType
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -42,6 +42,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.withContext
 
@@ -52,7 +53,7 @@
  */
 interface FingerprintPropertyRepository {
     /** Whether the fingerprint properties have been initialized yet. */
-    val propertiesInitialized: StateFlow<Boolean>
+    val propertiesInitialized: Flow<Boolean>
 
     /** The id of fingerprint sensor. */
     val sensorId: Flow<Int>
@@ -110,14 +111,8 @@
                 initialValue = UNINITIALIZED_PROPS,
             )
 
-    override val propertiesInitialized: StateFlow<Boolean> =
-        props
-            .map { it != UNINITIALIZED_PROPS }
-            .stateIn(
-                applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = props.value != UNINITIALIZED_PROPS,
-            )
+    override val propertiesInitialized: Flow<Boolean> =
+        props.map { it != UNINITIALIZED_PROPS }.onStart { emit(props.value != UNINITIALIZED_PROPS) }
 
     override val sensorId: Flow<Int> = props.map { it.sensorId }
 
@@ -141,7 +136,7 @@
 
     companion object {
         private const val TAG = "FingerprintPropertyRepositoryImpl"
-        private val UNINITIALIZED_PROPS =
+        val UNINITIALIZED_PROPS =
             FingerprintSensorPropertiesInternal(
                 -2 /* sensorId */,
                 SensorProperties.STRENGTH_CONVENIENCE,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
index f659ff0..58b238b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
@@ -16,12 +16,8 @@
 
 package com.android.systemui.biometrics.data.repository
 
-import android.hardware.biometrics.Flags
 import android.hardware.biometrics.PromptInfo
-import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.AuthController
-import com.android.systemui.biometrics.Utils
-import com.android.systemui.biometrics.Utils.isDeviceCredentialAllowed
 import com.android.systemui.biometrics.shared.model.PromptKind
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -56,8 +52,8 @@
     /** The gatekeeper challenge, if one is associated with this prompt. */
     val challenge: StateFlow<Long?>
 
-    /** The kind of credential to use (biometric, pin, pattern, etc.). */
-    val kind: StateFlow<PromptKind>
+    /** The kind of prompt to use (biometric, pin, pattern, etc.). */
+    val promptKind: StateFlow<PromptKind>
 
     /** The package name that the prompt is called from. */
     val opPackageName: StateFlow<String?>
@@ -69,18 +65,6 @@
      */
     val isConfirmationRequired: Flow<Boolean>
 
-    /**
-     * If biometric prompt without icon needs to show for displaying content prior to credential
-     * view.
-     */
-    val showBpWithoutIconForCredential: StateFlow<Boolean>
-
-    /**
-     * Update whether biometric prompt without icon needs to show for displaying content prior to
-     * credential view, which should be set before [setPrompt].
-     */
-    fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo)
-
     /** Update the prompt configuration, which should be set before [isShowing]. */
     fun setPrompt(
         promptInfo: PromptInfo,
@@ -125,8 +109,8 @@
     private val _userId: MutableStateFlow<Int?> = MutableStateFlow(null)
     override val userId = _userId.asStateFlow()
 
-    private val _kind: MutableStateFlow<PromptKind> = MutableStateFlow(PromptKind.None)
-    override val kind = _kind.asStateFlow()
+    private val _promptKind: MutableStateFlow<PromptKind> = MutableStateFlow(PromptKind.None)
+    override val promptKind = _promptKind.asStateFlow()
 
     private val _opPackageName: MutableStateFlow<String?> = MutableStateFlow(null)
     override val opPackageName = _opPackageName.asStateFlow()
@@ -145,21 +129,6 @@
             }
             .distinctUntilChanged()
 
-    private val _showBpWithoutIconForCredential: MutableStateFlow<Boolean> = MutableStateFlow(false)
-    override val showBpWithoutIconForCredential = _showBpWithoutIconForCredential.asStateFlow()
-
-    override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
-        val hasCredentialViewShown = kind.value.isCredential()
-        val showBpForCredential =
-            Flags.customBiometricPrompt() &&
-                constraintBp() &&
-                !Utils.isBiometricAllowed(promptInfo) &&
-                isDeviceCredentialAllowed(promptInfo) &&
-                promptInfo.contentView != null &&
-                !promptInfo.isContentViewMoreOptionsButtonUsed
-        _showBpWithoutIconForCredential.value = showBpForCredential && !hasCredentialViewShown
-    }
-
     override fun setPrompt(
         promptInfo: PromptInfo,
         userId: Int,
@@ -167,7 +136,7 @@
         kind: PromptKind,
         opPackageName: String,
     ) {
-        _kind.value = kind
+        _promptKind.value = kind
         _userId.value = userId
         _challenge.value = gatekeeperChallenge
         _promptInfo.value = promptInfo
@@ -178,7 +147,7 @@
         _promptInfo.value = null
         _userId.value = null
         _challenge.value = null
-        _kind.value = PromptKind.None
+        _promptKind.value = PromptKind.None
         _opPackageName.value = null
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
index 6e79e46..83aefca 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.biometrics.domain.interactor
 
 import android.app.ActivityTaskManager
+import android.util.Log
 import com.android.systemui.biometrics.data.repository.BiometricStatusRepository
 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
 import com.android.systemui.biometrics.shared.model.AuthenticationReason
@@ -26,6 +27,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.onEach
 
 /** Encapsulates business logic for interacting with biometric authentication state. */
 interface BiometricStatusInteractor {
@@ -49,15 +51,20 @@
 
     override val sfpsAuthenticationReason: Flow<AuthenticationReason> =
         combine(
-            biometricStatusRepository.fingerprintAuthenticationReason,
-            fingerprintPropertyRepository.sensorType
-        ) { reason: AuthenticationReason, sensorType ->
-            if (sensorType.isPowerButton() && reason.isReasonToAlwaysUpdateSfpsOverlay(activityTaskManager)) {
-                reason
-            } else {
-                AuthenticationReason.NotRunning
+                biometricStatusRepository.fingerprintAuthenticationReason,
+                fingerprintPropertyRepository.sensorType
+            ) { reason: AuthenticationReason, sensorType ->
+                if (
+                    sensorType.isPowerButton() &&
+                        reason.isReasonToAlwaysUpdateSfpsOverlay(activityTaskManager)
+                ) {
+                    reason
+                } else {
+                    AuthenticationReason.NotRunning
+                }
             }
-        }.distinctUntilChanged()
+            .distinctUntilChanged()
+            .onEach { Log.d(TAG, "sfpsAuthenticationReason updated: $it") }
 
     override val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus> =
         biometricStatusRepository.fingerprintAcquiredStatus
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
index 3112b67..d5b450d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
@@ -46,7 +46,7 @@
     displayStateInteractor: DisplayStateInteractor,
     udfpsOverlayInteractor: UdfpsOverlayInteractor,
 ) {
-    val propertiesInitialized: StateFlow<Boolean> = repository.propertiesInitialized
+    val propertiesInitialized: Flow<Boolean> = repository.propertiesInitialized
     val isUdfps: StateFlow<Boolean> =
         repository.sensorType
             .map { it.isUdfps() }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
index f8fb7bb..14ba8a2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
@@ -66,13 +66,13 @@
                 biometricPromptRepository.promptInfo,
                 biometricPromptRepository.challenge,
                 biometricPromptRepository.userId,
-                biometricPromptRepository.kind
-            ) { promptInfo, challenge, userId, kind ->
+                biometricPromptRepository.promptKind
+            ) { promptInfo, challenge, userId, promptKind ->
                 if (promptInfo == null || userId == null || challenge == null) {
                     return@combine null
                 }
 
-                when (kind) {
+                when (promptKind) {
                     PromptKind.Pin ->
                         BiometricPromptRequest.Credential.Pin(
                             info = promptInfo,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index deb47d3..4ba780f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -16,8 +16,10 @@
 
 package com.android.systemui.biometrics.domain.interactor
 
+import android.hardware.biometrics.Flags
 import android.hardware.biometrics.PromptInfo
 import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.Utils.getCredentialType
 import com.android.systemui.biometrics.Utils.isDeviceCredentialAllowed
 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
@@ -52,12 +54,16 @@
     /** Static metadata about the current prompt. */
     val prompt: Flow<BiometricPromptRequest.Biometric?>
 
+    /** The kind of prompt to use (biometric, pin, pattern, etc.). */
+    val promptKind: StateFlow<PromptKind>
+
     /** If using a credential is allowed. */
     val isCredentialAllowed: Flow<Boolean>
 
     /**
-     * The kind of credential the user may use as a fallback or [PromptKind.Biometric] if unknown or
-     * not [isCredentialAllowed].
+     * The kind of credential the user may use as a fallback or [PromptKind.None] if unknown or not
+     * [isCredentialAllowed]. This is separate from [promptKind], even if [promptKind] is
+     * [PromptKind.Biometric], [credentialKind] should still be one of pin/pattern/password.
      */
     val credentialKind: Flow<PromptKind>
 
@@ -70,34 +76,20 @@
     /** Fingerprint sensor type */
     val sensorType: Flow<FingerprintSensorType>
 
-    /**
-     * If biometric prompt without icon needs to show for displaying content prior to credential
-     * view.
-     */
-    val showBpWithoutIconForCredential: StateFlow<Boolean>
+    /** Switch to the credential view. */
+    fun onSwitchToCredential()
 
     /**
-     * Update whether biometric prompt without icon needs to show for displaying content prior to
-     * credential view, which should be set before [PromptRepository.setPrompt].
+     * Update the kind of prompt (biometric prompt w/ or w/o sensor icon, pin view, pattern view,
+     * etc).
      */
-    fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo)
-
-    /** Use biometrics for authentication. */
-    fun useBiometricsForAuthentication(
+    fun setPrompt(
         promptInfo: PromptInfo,
-        userId: Int,
-        challenge: Long,
+        effectiveUserId: Int,
         modalities: BiometricModalities,
-        opPackageName: String,
-    )
-
-    /** Use credential-based authentication instead of biometrics. */
-    fun useCredentialsForAuthentication(
-        promptInfo: PromptInfo,
-        kind: PromptKind,
-        userId: Int,
         challenge: Long,
         opPackageName: String,
+        onSwitchToCredential: Boolean,
     )
 
     /** Unset the current authentication request. */
@@ -110,7 +102,7 @@
 constructor(
     fingerprintPropertyRepository: FingerprintPropertyRepository,
     private val promptRepository: PromptRepository,
-    lockPatternUtils: LockPatternUtils,
+    private val lockPatternUtils: LockPatternUtils,
 ) : PromptSelectorInteractor {
 
     override val prompt: Flow<BiometricPromptRequest.Biometric?> =
@@ -118,7 +110,7 @@
             promptRepository.promptInfo,
             promptRepository.challenge,
             promptRepository.userId,
-            promptRepository.kind,
+            promptRepository.promptKind,
             promptRepository.opPackageName,
         ) { promptInfo, challenge, userId, kind, opPackageName ->
             if (
@@ -140,6 +132,8 @@
             }
         }
 
+    override val promptKind: StateFlow<PromptKind> = promptRepository.promptKind
+
     override val isConfirmationRequired: Flow<Boolean> =
         promptRepository.isConfirmationRequired.distinctUntilChanged()
 
@@ -153,44 +147,57 @@
             if (prompt != null && isAllowed) {
                 getCredentialType(lockPatternUtils, prompt.userInfo.deviceCredentialOwnerId)
             } else {
-                PromptKind.Biometric()
+                PromptKind.None
             }
         }
 
     override val sensorType: Flow<FingerprintSensorType> = fingerprintPropertyRepository.sensorType
 
-    override val showBpWithoutIconForCredential = promptRepository.showBpWithoutIconForCredential
-
-    override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
-        promptRepository.setShouldShowBpWithoutIconForCredential(promptInfo)
-    }
-
-    override fun useBiometricsForAuthentication(
-        promptInfo: PromptInfo,
-        userId: Int,
-        challenge: Long,
-        modalities: BiometricModalities,
-        opPackageName: String,
-    ) {
-        promptRepository.setPrompt(
-            promptInfo = promptInfo,
-            userId = userId,
-            gatekeeperChallenge = challenge,
-            kind = PromptKind.Biometric(modalities),
-            opPackageName = opPackageName,
+    override fun onSwitchToCredential() {
+        val modalities: BiometricModalities =
+            if (promptRepository.promptKind.value.isBiometric())
+                (promptRepository.promptKind.value as PromptKind.Biometric).activeModalities
+            else BiometricModalities()
+        setPrompt(
+            promptRepository.promptInfo.value!!,
+            promptRepository.userId.value!!,
+            modalities,
+            promptRepository.challenge.value!!,
+            promptRepository.opPackageName.value!!,
+            true /*onSwitchToCredential*/
         )
     }
 
-    override fun useCredentialsForAuthentication(
+    override fun setPrompt(
         promptInfo: PromptInfo,
-        kind: PromptKind,
-        userId: Int,
+        effectiveUserId: Int,
+        modalities: BiometricModalities,
         challenge: Long,
         opPackageName: String,
+        onSwitchToCredential: Boolean,
     ) {
+        val hasCredentialViewShown = promptKind.value.isCredential()
+        val showBpForCredential =
+            Flags.customBiometricPrompt() &&
+                com.android.systemui.Flags.constraintBp() &&
+                !Utils.isBiometricAllowed(promptInfo) &&
+                isDeviceCredentialAllowed(promptInfo) &&
+                promptInfo.contentView != null &&
+                !promptInfo.isContentViewMoreOptionsButtonUsed
+        val showBpWithoutIconForCredential = showBpForCredential && !hasCredentialViewShown
+        var kind: PromptKind = PromptKind.None
+        if (onSwitchToCredential) {
+            kind = getCredentialType(lockPatternUtils, effectiveUserId)
+        } else if (Utils.isBiometricAllowed(promptInfo) || showBpWithoutIconForCredential) {
+            // TODO(b/330908557): check to show one pane or two pane
+            kind = PromptKind.Biometric(modalities)
+        } else if (isDeviceCredentialAllowed(promptInfo)) {
+            kind = getCredentialType(lockPatternUtils, effectiveUserId)
+        }
+
         promptRepository.setPrompt(
             promptInfo = promptInfo,
-            userId = userId,
+            userId = effectiveUserId,
             gatekeeperChallenge = challenge,
             kind = kind,
             opPackageName = opPackageName,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index 6f079e2..4f96c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.biometrics.domain.model
 
+import android.content.ComponentName
 import android.graphics.Bitmap
 import android.hardware.biometrics.PromptContentView
 import android.hardware.biometrics.PromptInfo
@@ -43,6 +44,9 @@
         val logoBitmap: Bitmap? = info.logoBitmap
         val logoDescription: String? = info.logoDescription
         val negativeButtonText: String = info.negativeButtonText?.toString() ?: ""
+        val componentNameForConfirmDeviceCredentialActivity: ComponentName? =
+            info.componentNameForConfirmDeviceCredentialActivity
+        val allowBackgroundAuthentication = info.isAllowBackgroundAuthentication
     }
 
     /** Prompt using a credential (pin, pattern, password). */
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 da56951..65c5b6b 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
@@ -230,7 +230,7 @@
             }
 
             lifecycleScope.launch {
-                viewModel.showBpWithoutIconForCredential.collect { showWithoutIcon ->
+                viewModel.hideSensorIcon.collect { showWithoutIcon ->
                     if (!showWithoutIcon) {
                         PromptIconViewBinder.bind(
                             iconView,
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 d1ad783..13ea3f5 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
@@ -55,6 +55,7 @@
 import com.android.systemui.res.R
 import kotlin.math.abs
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
 
 /** Helper for [BiometricViewBinder] to handle resize transitions. */
@@ -169,14 +170,14 @@
             val flipConstraintSet = ConstraintSet()
 
             view.doOnLayout {
-                fun setVisibilities(size: PromptSize) {
+                fun setVisibilities(hideSensorIcon: Boolean, size: PromptSize) {
                     viewsToHideWhenSmall.forEach { it.showContentOrHide(forceHide = size.isSmall) }
                     largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
                     largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
                     largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
                     largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
 
-                    if (viewModel.showBpWithoutIconForCredential.value) {
+                    if (hideSensorIcon) {
                         smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
                         smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
                         smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
@@ -198,29 +199,32 @@
                                     iconParams.leftMargin = position.left
                                     mediumConstraintSet.clear(
                                         R.id.biometric_icon,
-                                        ConstraintSet.END
+                                        ConstraintSet.RIGHT
                                     )
                                     mediumConstraintSet.connect(
                                         R.id.biometric_icon,
-                                        ConstraintSet.START,
+                                        ConstraintSet.LEFT,
                                         ConstraintSet.PARENT_ID,
-                                        ConstraintSet.START
+                                        ConstraintSet.LEFT
                                     )
                                     mediumConstraintSet.setMargin(
                                         R.id.biometric_icon,
-                                        ConstraintSet.START,
+                                        ConstraintSet.LEFT,
                                         position.left
                                     )
-                                    smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.END)
+                                    smallConstraintSet.clear(
+                                        R.id.biometric_icon,
+                                        ConstraintSet.RIGHT
+                                    )
                                     smallConstraintSet.connect(
                                         R.id.biometric_icon,
-                                        ConstraintSet.START,
+                                        ConstraintSet.LEFT,
                                         ConstraintSet.PARENT_ID,
-                                        ConstraintSet.START
+                                        ConstraintSet.LEFT
                                     )
                                     smallConstraintSet.setMargin(
                                         R.id.biometric_icon,
-                                        ConstraintSet.START,
+                                        ConstraintSet.LEFT,
                                         position.left
                                     )
                                 }
@@ -251,32 +255,32 @@
                                     iconParams.rightMargin = position.right
                                     mediumConstraintSet.clear(
                                         R.id.biometric_icon,
-                                        ConstraintSet.START
+                                        ConstraintSet.LEFT
                                     )
                                     mediumConstraintSet.connect(
                                         R.id.biometric_icon,
-                                        ConstraintSet.END,
+                                        ConstraintSet.RIGHT,
                                         ConstraintSet.PARENT_ID,
-                                        ConstraintSet.END
+                                        ConstraintSet.RIGHT
                                     )
                                     mediumConstraintSet.setMargin(
                                         R.id.biometric_icon,
-                                        ConstraintSet.END,
+                                        ConstraintSet.RIGHT,
                                         position.right
                                     )
                                     smallConstraintSet.clear(
                                         R.id.biometric_icon,
-                                        ConstraintSet.START
+                                        ConstraintSet.LEFT
                                     )
                                     smallConstraintSet.connect(
                                         R.id.biometric_icon,
-                                        ConstraintSet.END,
+                                        ConstraintSet.RIGHT,
                                         ConstraintSet.PARENT_ID,
-                                        ConstraintSet.END
+                                        ConstraintSet.RIGHT
                                     )
                                     smallConstraintSet.setMargin(
                                         R.id.biometric_icon,
-                                        ConstraintSet.END,
+                                        ConstraintSet.RIGHT,
                                         position.right
                                     )
                                 }
@@ -362,12 +366,16 @@
                             }
                         }
                     }
+                    lifecycleScope.launch {
+                        combine(viewModel.hideSensorIcon, viewModel.size, ::Pair).collect {
+                            (hideSensorIcon, size) ->
+                            setVisibilities(hideSensorIcon, size)
+                        }
+                    }
 
                     lifecycleScope.launch {
                         combine(viewModel.position, viewModel.size, ::Pair).collect {
                             (position, size) ->
-                            setVisibilities(size)
-
                             if (position.isLeft) {
                                 if (size.isSmall) {
                                     flipConstraintSet.clone(smallConstraintSet)
@@ -378,15 +386,15 @@
                                 // Move all content to other panel
                                 flipConstraintSet.connect(
                                     R.id.scrollView,
-                                    ConstraintSet.START,
+                                    ConstraintSet.LEFT,
                                     R.id.midGuideline,
-                                    ConstraintSet.START
+                                    ConstraintSet.LEFT
                                 )
                                 flipConstraintSet.connect(
                                     R.id.scrollView,
-                                    ConstraintSet.END,
+                                    ConstraintSet.RIGHT,
                                     R.id.rightGuideline,
-                                    ConstraintSet.END
+                                    ConstraintSet.RIGHT
                                 )
                             }
 
@@ -481,7 +489,7 @@
                                 v.showContentOrHide(forceHide = size.isSmall)
                             }
 
-                            if (viewModel.showBpWithoutIconForCredential.value) {
+                            if (viewModel.hideSensorIcon.first()) {
                                 iconHolderView.visibility = View.GONE
                             }
 
@@ -492,10 +500,6 @@
                                 viewsToFadeInOnSizeChange.forEach { it.alpha = 0f }
                             }
 
-                            // TODO(b/302735104): Fix wrong height due to the delay of
-                            // PromptContentView. addOnLayoutChangeListener() will cause crash
-                            // when showing credential view, since |PromptIconViewModel| won't
-                            // release the flow.
                             // propagate size changes to legacy panel controller and animate
                             // transitions
                             view.doOnLayout {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index 4bdbfa2..ff7ac35 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.graphics.PorterDuff
 import android.graphics.PorterDuffColorFilter
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.WindowManager
@@ -91,6 +92,13 @@
                                     showIndicatorForDeviceEntry,
                                     progressBarIsVisible) =
                                     combinedFlows
+                                Log.d(
+                                    TAG,
+                                    "systemServerAuthReason = $systemServerAuthReason, " +
+                                        "showIndicatorForDeviceEntry = " +
+                                        "$showIndicatorForDeviceEntry, " +
+                                        "progressBarIsVisible = $progressBarIsVisible"
+                                )
                                 if (!isInRearDisplayMode) {
                                     if (progressBarIsVisible) {
                                         hide()
@@ -114,6 +122,10 @@
     /** Show the side fingerprint sensor indicator */
     private fun show() {
         if (overlayView?.isAttachedToWindow == true) {
+            Log.d(
+                TAG,
+                "show(): overlayView $overlayView isAttachedToWindow already, ignoring show request"
+            )
             return
         }
 
@@ -128,6 +140,7 @@
             )
         bind(overlayView!!, overlayViewModel, fpsUnlockTracker.get(), windowManager.get())
         overlayView!!.visibility = View.INVISIBLE
+        Log.d(TAG, "show(): adding overlayView $overlayView")
         windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams)
     }
 
@@ -137,6 +150,7 @@
             val lottie = overlayView!!.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
             lottie.pauseAnimation()
             lottie.removeAllLottieOnCompositionLoadedListener()
+            Log.d(TAG, "hide(): removing overlayView $overlayView, setting to null")
             windowManager.get().removeView(overlayView)
             overlayView = null
         }
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 2104f3e..156ec6b 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
@@ -16,7 +16,10 @@
 
 package com.android.systemui.biometrics.ui.viewmodel
 
+import android.app.ActivityTaskManager
+import android.content.ComponentName
 import android.content.Context
+import android.content.pm.ActivityInfo
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import android.graphics.Rect
@@ -26,18 +29,22 @@
 import android.hardware.biometrics.BiometricPrompt
 import android.hardware.biometrics.Flags.customBiometricPrompt
 import android.hardware.biometrics.PromptContentView
+import android.os.UserHandle
 import android.util.Log
 import android.util.RotationUtils
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
+import com.android.launcher3.icons.IconProvider
 import com.android.systemui.Flags.bpTalkback
 import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.UdfpsUtils
 import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.Utils.isSystem
 import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
+import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
 import com.android.systemui.biometrics.shared.model.BiometricModalities
 import com.android.systemui.biometrics.shared.model.BiometricModality
 import com.android.systemui.biometrics.shared.model.DisplayRotation
@@ -67,11 +74,13 @@
 @Inject
 constructor(
     displayStateInteractor: DisplayStateInteractor,
-    promptSelectorInteractor: PromptSelectorInteractor,
+    private val promptSelectorInteractor: PromptSelectorInteractor,
     @Application private val context: Context,
     private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
     private val biometricStatusInteractor: BiometricStatusInteractor,
-    private val udfpsUtils: UdfpsUtils
+    private val udfpsUtils: UdfpsUtils,
+    private val iconProvider: IconProvider,
+    private val activityTaskManager: ActivityTaskManager,
 ) {
     /** The set of modalities available for this prompt */
     val modalities: Flow<BiometricModalities> =
@@ -195,8 +204,11 @@
     /** The kind of credential the user has. */
     val credentialKind: Flow<PromptKind> = promptSelectorInteractor.credentialKind
 
-    val showBpWithoutIconForCredential: StateFlow<Boolean> =
-        promptSelectorInteractor.showBpWithoutIconForCredential
+    /** The kind of prompt to use (biometric, pin, pattern, etc.). */
+    val promptKind: StateFlow<PromptKind> = promptSelectorInteractor.promptKind
+
+    /** Whether the sensor icon on biometric prompt ui should be hidden. */
+    val hideSensorIcon: Flow<Boolean> = modalities.map { it.isEmpty }.distinctUntilChanged()
 
     /** The label to use for the cancel button. */
     val negativeButtonText: Flow<String> =
@@ -496,14 +508,7 @@
                     !(customBiometricPrompt() && constraintBp()) || it == null -> null
                     it.logoRes != -1 -> context.resources.getDrawable(it.logoRes, context.theme)
                     it.logoBitmap != null -> BitmapDrawable(context.resources, it.logoBitmap)
-                    else ->
-                        try {
-                            val info = context.getApplicationInfo(it.opPackageName)
-                            context.packageManager.getApplicationIcon(info)
-                        } catch (e: Exception) {
-                            Log.w(TAG, "Cannot find icon for package " + it.opPackageName, e)
-                            null
-                        }
+                    else -> context.getUserBadgedIcon(it, iconProvider, activityTaskManager)
                 }
             }
             .distinctUntilChanged()
@@ -514,15 +519,8 @@
             .map {
                 when {
                     !(customBiometricPrompt() && constraintBp()) || it == null -> ""
-                    it.logoDescription != null -> it.logoDescription
-                    else ->
-                        try {
-                            val info = context.getApplicationInfo(it.opPackageName)
-                            context.packageManager.getApplicationLabel(info).toString()
-                        } catch (e: Exception) {
-                            Log.w(TAG, "Cannot find name for package " + it.opPackageName, e)
-                            ""
-                        }
+                    !it.logoDescription.isNullOrEmpty() -> it.logoDescription
+                    else -> context.getUserBadgedLabel(it, activityTaskManager)
                 }
             }
             .distinctUntilChanged()
@@ -896,6 +894,7 @@
      */
     fun onSwitchToCredential() {
         _forceLargeSize.value = true
+        promptSelectorInteractor.onSwitchToCredential()
     }
 
     private fun vibrateOnSuccess() {
@@ -922,15 +921,109 @@
     }
 
     companion object {
-        private const val TAG = "PromptViewModel"
+        const val TAG = "PromptViewModel"
     }
 }
 
-private fun Context.getApplicationInfo(packageName: String): ApplicationInfo =
-    packageManager.getApplicationInfo(
-        packageName,
-        PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER
-    )
+private fun Context.getUserBadgedIcon(
+    prompt: BiometricPromptRequest.Biometric,
+    iconProvider: IconProvider,
+    activityTaskManager: ActivityTaskManager
+): Drawable? {
+    var icon: Drawable? = null
+    val componentName = prompt.getComponentNameForLogo(activityTaskManager)
+    if (componentName != null && shouldShowLogoWithOverrides(componentName)) {
+        val activityInfo = getActivityInfo(componentName)
+        icon = if (activityInfo == null) null else iconProvider.getIcon(activityInfo)
+    }
+    if (icon == null) {
+        val appInfo = prompt.getApplicationInfoForLogo(this, componentName)
+        if (appInfo == null) {
+            Log.w(PromptViewModel.TAG, "Cannot find app logo for package $opPackageName")
+            return null
+        } else {
+            icon = packageManager.getApplicationIcon(appInfo)
+        }
+    }
+    return packageManager.getUserBadgedIcon(icon, UserHandle.of(prompt.userInfo.userId))
+}
+
+private fun Context.getUserBadgedLabel(
+    prompt: BiometricPromptRequest.Biometric,
+    activityTaskManager: ActivityTaskManager
+): String {
+    val componentName = prompt.getComponentNameForLogo(activityTaskManager)
+    val appInfo = prompt.getApplicationInfoForLogo(this, componentName)
+    return if (appInfo == null || packageManager.getApplicationLabel(appInfo).isNullOrEmpty()) {
+        Log.w(PromptViewModel.TAG, "Cannot find app logo for package $opPackageName")
+        ""
+    } else {
+        packageManager
+            .getUserBadgedLabel(packageManager.getApplicationLabel(appInfo), UserHandle.of(userId))
+            .toString()
+    }
+}
+
+private fun BiometricPromptRequest.Biometric.getComponentNameForLogo(
+    activityTaskManager: ActivityTaskManager
+): ComponentName? {
+    val topActivity: ComponentName? = activityTaskManager.getTasks(1).firstOrNull()?.topActivity
+    return when {
+        componentNameForConfirmDeviceCredentialActivity != null ->
+            componentNameForConfirmDeviceCredentialActivity
+        topActivity?.packageName.contentEquals(opPackageName) -> topActivity
+        else -> {
+            Log.w(PromptViewModel.TAG, "Top activity $topActivity is not the client $opPackageName")
+            null
+        }
+    }
+}
+
+private fun BiometricPromptRequest.Biometric.getApplicationInfoForLogo(
+    context: Context,
+    componentNameForLogo: ComponentName?
+): ApplicationInfo? {
+    val packageName =
+        when {
+            componentNameForLogo != null -> componentNameForLogo.packageName
+            // TODO(b/339532378): We should check whether |allowBackgroundAuthentication| should be
+            // removed.
+            // This is being consistent with the check in [AuthController.showDialog()].
+            allowBackgroundAuthentication || isSystem(context, opPackageName) -> opPackageName
+            else -> null
+        }
+    return if (packageName == null) {
+        Log.w(PromptViewModel.TAG, "Cannot find application info for $opPackageName")
+        null
+    } else {
+        context.getApplicationInfo(packageName)
+    }
+}
+
+private fun Context.shouldShowLogoWithOverrides(componentName: ComponentName): Boolean {
+    return resources
+        .getStringArray(R.array.biometric_dialog_package_names_for_logo_with_overrides)
+        .find { componentName.packageName.contentEquals(it) } != null
+}
+
+private fun Context.getActivityInfo(componentName: ComponentName): ActivityInfo? =
+    try {
+        packageManager.getActivityInfo(componentName, 0)
+    } catch (e: PackageManager.NameNotFoundException) {
+        Log.w(PromptViewModel.TAG, "Cannot find activity info for $opPackageName", e)
+        null
+    }
+
+private fun Context.getApplicationInfo(packageName: String): ApplicationInfo? =
+    try {
+        packageManager.getApplicationInfo(
+            packageName,
+            PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER
+        )
+    } catch (e: PackageManager.NameNotFoundException) {
+        Log.w(PromptViewModel.TAG, "Cannot find application info for $opPackageName", e)
+        null
+    }
 
 /** How the fingerprint sensor was started for the prompt. */
 enum class FingerprintStartMode {
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
index dd8c0df..911145b 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -204,11 +204,7 @@
         isEnabled: Boolean,
         @StringRes infoResId: Int
     ) {
-        getAutoOnToggle(dialog).apply {
-            isChecked = isEnabled
-            setEnabled(true)
-            alpha = ENABLED_ALPHA
-        }
+        getAutoOnToggle(dialog).isChecked = isEnabled
         getAutoOnToggleInfoTextView(dialog).text = dialog.context.getString(infoResId)
     }
 
@@ -236,12 +232,8 @@
         }
 
         getAutoOnToggleView(dialog).visibility = initialUiProperties.autoOnToggleVisibility
-        getAutoOnToggle(dialog).setOnCheckedChangeListener { view, isChecked ->
+        getAutoOnToggle(dialog).setOnCheckedChangeListener { _, isChecked ->
             mutableBluetoothAutoOnToggle.value = isChecked
-            view.apply {
-                isEnabled = false
-                alpha = DISABLED_ALPHA
-            }
             uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_AUTO_ON_TOGGLE_CLICKED)
         }
     }
@@ -427,8 +419,7 @@
         const val ACTION_PREVIOUSLY_CONNECTED_DEVICE =
             "com.android.settings.PREVIOUSLY_CONNECTED_DEVICE"
         const val ACTION_PAIR_NEW_DEVICE = "android.settings.BLUETOOTH_PAIRING_SETTINGS"
-        const val ACTION_AUDIO_SHARING =
-            "com.google.android.settings.BLUETOOTH_AUDIO_SHARING_SETTINGS"
+        const val ACTION_AUDIO_SHARING = "com.android.settings.BLUETOOTH_AUDIO_SHARING_SETTINGS"
         const val DISABLED_ALPHA = 0.3f
         const val ENABLED_ALPHA = 1f
         const val PROGRESS_BAR_ANIMATION_DURATION_MS = 1500L
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt
index c018ecb..0544a4f 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt
@@ -18,6 +18,8 @@
 
 import android.content.Context
 import android.os.UserManager
+import com.android.settingslib.RestrictedLockUtils
+import com.android.systemui.Flags.enforceBrightnessBaseUserRestriction
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -66,7 +68,18 @@
                         user.id
                     )
                     ?.let { PolicyRestriction.Restricted(it) }
-                    ?: PolicyRestriction.NoRestriction
+                    ?: if (
+                        enforceBrightnessBaseUserRestriction() &&
+                            userRestrictionChecker.hasBaseUserRestriction(
+                                applicationContext,
+                                UserManager.DISALLOW_CONFIG_BRIGHTNESS,
+                                user.id
+                            )
+                    ) {
+                        PolicyRestriction.Restricted(RestrictedLockUtils.EnforcedAdmin())
+                    } else {
+                        PolicyRestriction.NoRestriction
+                    }
             }
             .flowOn(backgroundDispatcher)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
index c1be37a..a51d8ff 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
@@ -23,7 +23,6 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
@@ -32,6 +31,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformSlider
 import com.android.systemui.brightness.shared.GammaBrightness
 import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
@@ -107,10 +107,13 @@
     viewModel: BrightnessSliderViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val gamma: Int by viewModel.currentBrightness.map { it.value }.collectAsState(initial = 0)
+    val gamma: Int by
+        viewModel.currentBrightness.map { it.value }.collectAsStateWithLifecycle(initialValue = 0)
     val coroutineScope = rememberCoroutineScope()
     val restriction by
-        viewModel.policyRestriction.collectAsState(initial = PolicyRestriction.NoRestriction)
+        viewModel.policyRestriction.collectAsStateWithLifecycle(
+            initialValue = PolicyRestriction.NoRestriction
+        )
 
     BrightnessSlider(
         gammaValue = gamma,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index b42a903..2eca02c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -31,9 +31,12 @@
 import com.android.systemui.communal.domain.interactor.CommunalInteractor;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -88,6 +91,8 @@
     private final JavaAdapter mJavaAdapter;
     private final SystemClock mSystemClock;
     private final Lazy<SelectedUserInteractor> mUserInteractor;
+    private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor;
+    private final Lazy<SceneContainerOcclusionInteractor> mSceneContainerOcclusionInteractor;
 
     private int mState;
     private boolean mShowingAod;
@@ -170,7 +175,9 @@
             JavaAdapter javaAdapter,
             SystemClock systemClock,
             Lazy<SelectedUserInteractor> userInteractor,
-            Lazy<CommunalInteractor> communalInteractorLazy) {
+            Lazy<CommunalInteractor> communalInteractorLazy,
+            Lazy<DeviceEntryInteractor> deviceEntryInteractor,
+            Lazy<SceneContainerOcclusionInteractor> sceneContainerOcclusionInteractor) {
         mFalsingDataProvider = falsingDataProvider;
         mFalsingManager = falsingManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -186,6 +193,8 @@
         mSystemClock = systemClock;
         mUserInteractor = userInteractor;
         mCommunalInteractorLazy = communalInteractorLazy;
+        mDeviceEntryInteractor = deviceEntryInteractor;
+        mSceneContainerOcclusionInteractor = sceneContainerOcclusionInteractor;
     }
 
     @Override
@@ -196,7 +205,18 @@
         mStatusBarStateController.addCallback(mStatusBarStateListener);
         mState = mStatusBarStateController.getState();
 
-        mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
+        if (SceneContainerFlag.isEnabled()) {
+            mJavaAdapter.alwaysCollectFlow(
+                    mDeviceEntryInteractor.get().isDeviceEntered(),
+                    this::isDeviceEnteredChanged
+            );
+            mJavaAdapter.alwaysCollectFlow(
+                    mSceneContainerOcclusionInteractor.get().getInvisibleDueToOcclusion(),
+                    this::isInvisibleDueToOcclusionChanged
+            );
+        } else {
+            mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
+        }
 
         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
 
@@ -216,6 +236,14 @@
         mDockManager.addListener(mDockEventListener);
     }
 
+    public void isDeviceEnteredChanged(boolean unused) {
+        updateSensorRegistration();
+    }
+
+    public void isInvisibleDueToOcclusionChanged(boolean unused) {
+        updateSensorRegistration();
+    }
+
     @Override
     public void onSuccessfulUnlock() {
         logDebug("REAL: onSuccessfulUnlock");
@@ -302,7 +330,7 @@
     @Override
     public void onTouchEvent(MotionEvent ev) {
         logDebug("REAL: onTouchEvent(" + MotionEvent.actionToString(ev.getActionMasked()) + ")");
-        if (!mKeyguardStateController.isShowing()) {
+        if (!isKeyguardShowing()) {
             avoidGesture();
             return;
         }
@@ -399,12 +427,13 @@
     }
 
     private boolean shouldBeRegisteredToSensors() {
-        return mScreenOn
-                && (mState == StatusBarState.KEYGUARD
-                || (mState == StatusBarState.SHADE
-                && mKeyguardStateController.isOccluded()
-                && mKeyguardStateController.isShowing()))
-                && !mShowingAod;
+        final boolean isKeyguard = mState == StatusBarState.KEYGUARD;
+
+        final boolean isShadeOverOccludedKeyguard = mState == StatusBarState.SHADE
+                && isKeyguardShowing()
+                && isKeyguardOccluded();
+
+        return mScreenOn && !mShowingAod && (isKeyguard || isShadeOverOccludedKeyguard);
     }
 
     private void updateSensorRegistration() {
@@ -446,6 +475,32 @@
         mFalsingManager.onProximityEvent(new ProximityEventImpl(proximityEvent));
     }
 
+    /**
+     * Returns {@code true} if the keyguard is showing (whether or not the screen is on, whether or
+     * not an activity is occluding the keyguard, and whether or not the shade is open on top of the
+     * keyguard), or {@code false} if the user has dismissed the keyguard by authenticating or
+     * swiping up.
+     */
+    private boolean isKeyguardShowing() {
+        if (SceneContainerFlag.isEnabled()) {
+            return !mDeviceEntryInteractor.get().isDeviceEntered().getValue();
+        } else {
+            return mKeyguardStateController.isShowing();
+        }
+    }
+
+    /**
+     * Returns {@code true} if there is an activity display on top of ("occluding") the keyguard, or
+     * {@code false} if an activity is not occluding the keyguard (including if the keyguard is not
+     * showing at all).
+     */
+    private boolean isKeyguardOccluded() {
+        if (SceneContainerFlag.isEnabled()) {
+            return mSceneContainerOcclusionInteractor.get().getInvisibleDueToOcclusion().getValue();
+        } else {
+            return mKeyguardStateController.isOccluded();
+        }
+    }
 
     static void logDebug(String msg) {
         if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index 8efc66d..ba236ba 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -66,11 +66,11 @@
 import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance;
 import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel;
 
-import java.util.ArrayList;
-
 import kotlin.Unit;
 import kotlin.jvm.functions.Function0;
 
+import java.util.ArrayList;
+
 /**
  * Handles the visual elements and animations for the clipboard overlay.
  */
@@ -109,6 +109,7 @@
     private View mDismissButton;
     private LinearLayout mActionContainer;
     private ClipboardOverlayCallbacks mClipboardCallbacks;
+    private ActionButtonViewBinder mActionButtonViewBinder = new ActionButtonViewBinder();
 
     public ClipboardOverlayView(Context context) {
         this(context, null);
@@ -152,14 +153,15 @@
 
     private void bindDefaultActionChips() {
         if (screenshotShelfUi2()) {
-            ActionButtonViewBinder.INSTANCE.bind(mRemoteCopyChip,
+            mActionButtonViewBinder.bind(mRemoteCopyChip,
                     ActionButtonViewModel.Companion.withNextId(
                             new ActionButtonAppearance(
                                     Icon.createWithResource(mContext,
                                             R.drawable.ic_baseline_devices_24).loadDrawable(
                                             mContext),
                                     null,
-                                    mContext.getString(R.string.clipboard_send_nearby_description)),
+                                    mContext.getString(R.string.clipboard_send_nearby_description),
+                                    true),
                             new Function0<>() {
                                 @Override
                                 public Unit invoke() {
@@ -169,12 +171,14 @@
                                     return null;
                                 }
                             }));
-            ActionButtonViewBinder.INSTANCE.bind(mShareChip,
+            mActionButtonViewBinder.bind(mShareChip,
                     ActionButtonViewModel.Companion.withNextId(
                             new ActionButtonAppearance(
                                     Icon.createWithResource(mContext,
                                             R.drawable.ic_screenshot_share).loadDrawable(mContext),
-                                    null, mContext.getString(com.android.internal.R.string.share)),
+                                    null,
+                                    mContext.getString(com.android.internal.R.string.share),
+                                    true),
                             new Function0<>() {
                                 @Override
                                 public Unit invoke() {
@@ -512,9 +516,9 @@
     private View constructShelfActionChip(RemoteAction action, Runnable onFinish) {
         View chip = LayoutInflater.from(mContext).inflate(
                 R.layout.shelf_action_chip, mActionContainer, false);
-        ActionButtonViewBinder.INSTANCE.bind(chip, ActionButtonViewModel.Companion.withNextId(
+        mActionButtonViewBinder.bind(chip, ActionButtonViewModel.Companion.withNextId(
                 new ActionButtonAppearance(action.getIcon().loadDrawable(mContext),
-                        action.getTitle(), action.getTitle()), new Function0<>() {
+                        action.getTitle(), action.getTitle(), false), new Function0<>() {
                     @Override
                     public Unit invoke() {
                         try {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index f437032..6f20a8d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.communal
 
 import android.provider.Settings
-import android.service.dreams.Flags.dreamTracksFocus
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.CoreStartable
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
@@ -43,6 +42,7 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
@@ -74,6 +74,10 @@
 ) : CoreStartable {
     private var screenTimeout: Int = DEFAULT_SCREEN_TIMEOUT
 
+    private var timeoutJob: Job? = null
+
+    private var isDreaming: Boolean = false
+
     override fun start() {
         // Handle automatically switching based on keyguard state.
         keyguardTransitionInteractor.startedKeyguardTransitionStep
@@ -113,47 +117,67 @@
             }
             .launchIn(bgScope)
 
-        // Handle timing out back to the dream.
+        // The hub mode timeout should start as soon as the user enters hub mode. At the end of the
+        // timer, if the device is dreaming, hub mode should closed and reveal the dream. If the
+        // dream is not running, nothing will happen. However if the dream starts again underneath
+        // hub mode after the initial timeout expires, such as if the device is docked or the dream
+        // app is updated by the Play store, a new timeout should be started.
         bgScope.launch {
             combine(
                     communalInteractor.desiredScene,
                     // Emit a value on start so the combine starts.
                     communalInteractor.userActivity.emitOnStart()
                 ) { scene, _ ->
-                    // Time out should run whenever we're dreaming and the hub is open, even if not
-                    // docked.
+                    // Only timeout if we're on the hub is open.
                     scene == CommunalScenes.Communal
                 }
-                // mapLatest cancels the previous action block when new values arrive, so any
-                // already running timeout gets cancelled when conditions change or user interaction
-                // is detected.
-                .mapLatest { shouldTimeout ->
-                    if (!shouldTimeout) {
-                        return@mapLatest false
+                .collectLatest { shouldTimeout ->
+                    cancelHubTimeout()
+                    if (shouldTimeout) {
+                        startHubTimeout()
                     }
-
-                    delay(screenTimeout.milliseconds)
-                    true
                 }
-                .sample(keyguardInteractor.isDreaming, ::Pair)
-                .collect { (shouldTimeout, isDreaming) ->
-                    if (isDreaming && shouldTimeout) {
+        }
+        bgScope.launch {
+            keyguardInteractor.isDreaming
+                .sample(communalInteractor.desiredScene, ::Pair)
+                .collectLatest { (isDreaming, scene) ->
+                    this@CommunalSceneStartable.isDreaming = isDreaming
+                    if (scene == CommunalScenes.Communal && isDreaming && timeoutJob == null) {
+                        // If dreaming starts after timeout has expired, ex. if dream restarts under
+                        // the hub, just close the hub immediately.
                         communalInteractor.changeScene(CommunalScenes.Blank)
                     }
                 }
         }
 
-        if (dreamTracksFocus()) {
-            bgScope.launch {
-                communalInteractor.isIdleOnCommunal.collectLatest {
-                    withContext(mainDispatcher) {
-                        notificationShadeWindowController.setGlanceableHubShowing(it)
-                    }
+        bgScope.launch {
+            communalInteractor.isIdleOnCommunal.collectLatest {
+                withContext(mainDispatcher) {
+                    notificationShadeWindowController.setGlanceableHubShowing(it)
                 }
             }
         }
     }
 
+    private fun cancelHubTimeout() {
+        timeoutJob?.cancel()
+        timeoutJob = null
+    }
+
+    private fun startHubTimeout() {
+        if (timeoutJob == null) {
+            timeoutJob =
+                bgScope.launch {
+                    delay(screenTimeout.milliseconds)
+                    if (isDreaming) {
+                        communalInteractor.changeScene(CommunalScenes.Blank)
+                    }
+                    timeoutJob = null
+                }
+        }
+    }
+
     private suspend fun determineSceneAfterTransition(
         lastStartedTransition: TransitionStep,
     ): SceneKey? {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
index 03f54c8..5cd15f2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
@@ -17,15 +17,23 @@
 package com.android.systemui.communal.data.model
 
 import android.appwidget.AppWidgetProviderInfo
+import com.android.settingslib.flags.Flags.allowAllWidgetsOnLockscreenByDefault
 
 /**
  * The widget categories to display on communal hub (where categories is a bitfield with values that
  * match those in {@link AppWidgetProviderInfo}).
  */
 @JvmInline
-value class CommunalWidgetCategories(
-    // The default is keyguard widgets.
-    val categories: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
-) {
+value class CommunalWidgetCategories(val categories: Int = defaultCategories) {
     fun contains(category: Int) = (categories and category) == category
+
+    companion object {
+        val defaultCategories: Int
+            get() {
+                return AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD or
+                    if (allowAllWidgetsOnLockscreenByDefault())
+                        AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN
+                    else 0
+            }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
index e2fed6d..e5a0e50 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -53,7 +53,7 @@
                 updateMediaModel(data)
             }
 
-            override fun onMediaDataRemoved(key: String) {
+            override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
                 updateMediaModel()
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 9debe0e..88cb64c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -18,7 +18,6 @@
 
 import android.app.admin.DevicePolicyManager
 import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
-import android.appwidget.AppWidgetProviderInfo
 import android.content.IntentFilter
 import android.content.pm.UserInfo
 import android.provider.Settings
@@ -108,10 +107,9 @@
             .onStart { emit(Unit) }
             .map {
                 CommunalWidgetCategories(
-                    // The default is to show only keyguard widgets.
                     secureSettings.getIntForUser(
                         GLANCEABLE_HUB_CONTENT_SETTING,
-                        AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
+                        CommunalWidgetCategories.defaultCategories,
                         user.id
                     )
                 )
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 06c8396..9599a88 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
@@ -61,7 +61,6 @@
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.smartspace.data.repository.SmartspaceRepository
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
-import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import com.android.systemui.util.kotlin.emitOnStart
 import javax.inject.Inject
@@ -130,7 +129,7 @@
         allOf(
                 communalSettingsInteractor.isCommunalEnabled,
                 not(keyguardInteractor.isEncryptedOrLockdown),
-                anyOf(keyguardInteractor.isKeyguardShowing, keyguardInteractor.isDreaming)
+                keyguardInteractor.isKeyguardShowing
             )
             .distinctUntilChanged()
             .onEach { available ->
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index f9de609..3e5126a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -75,7 +75,7 @@
                 scope = bgScope,
                 // Start this eagerly since the value can be accessed synchronously.
                 started = SharingStarted.Eagerly,
-                initialValue = CommunalWidgetCategories().categories
+                initialValue = CommunalWidgetCategories.defaultCategories
             )
 
     private val workProfileUserInfoCallbackFlow: Flow<UserInfo?> = conflatedCallbackFlow {
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 f6122ad..c0dc313 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
@@ -96,6 +96,8 @@
         uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
     }
 
+    val isIdleOnCommunal: StateFlow<Boolean> = communalInteractor.isIdleOnCommunal
+
     /** Launch the widget picker activity using the given {@link ActivityResultLauncher}. */
     suspend fun onOpenWidgetPicker(
         resources: Resources,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 1120466..97db43b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.log.LogBuffer
@@ -64,10 +64,10 @@
     @Application private val scope: CoroutineScope,
     @Main private val resources: Resources,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    keyguardInteractor: KeyguardInteractor,
     private val communalInteractor: CommunalInteractor,
     tutorialInteractor: CommunalTutorialInteractor,
     private val shadeInteractor: ShadeInteractor,
-    deviceEntryInteractor: DeviceEntryInteractor,
     @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
     @CommunalLog logBuffer: LogBuffer,
 ) : BaseCommunalViewModel(communalInteractor, mediaHost) {
@@ -142,8 +142,6 @@
     val isEnableWorkProfileDialogShowing: Flow<Boolean> =
         _isEnableWorkProfileDialogShowing.asStateFlow()
 
-    val deviceUnlocked: Flow<Boolean> = deviceEntryInteractor.isUnlocked
-
     init {
         // Initialize our media host for the UMO. This only needs to happen once and must be done
         // before the MediaHierarchyManager attempts to move the UMO to the hub.
@@ -240,6 +238,14 @@
      */
     val touchesAllowed: Flow<Boolean> = not(shadeInteractor.isAnyFullyExpanded)
 
+    // TODO(b/339667383): remove this temporary swipe gesture handle
+    /**
+     * The dream overlay has its own gesture handle as the SysUI window is not visible above the
+     * dream. This flow will be false when dreaming so that we don't show a duplicate handle when
+     * opening the hub over the dream.
+     */
+    val showGestureIndicator: Flow<Boolean> = not(keyguardInteractor.isDreaming)
+
     companion object {
         const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
index 840c3a8..2559137 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.communal.widgets
 
 import android.appwidget.AppWidgetHostView
+import android.appwidget.AppWidgetProviderInfo
 import android.content.Context
 import android.graphics.Outline
 import android.graphics.Rect
@@ -50,6 +51,11 @@
         enforceRoundedCorners()
     }
 
+    override fun setAppWidget(appWidgetId: Int, info: AppWidgetProviderInfo?) {
+        super.setAppWidget(appWidgetId, info)
+        setPadding(0, 0, 0, 0)
+    }
+
     private val cornerRadiusEnforcementOutline: ViewOutlineProvider =
         object : ViewOutlineProvider() {
             override fun getOutline(view: View?, outline: Outline) {
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 f20fafc..426f484 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -44,6 +44,7 @@
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
 import javax.inject.Inject
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
 
 /** An Activity for editing the widgets that appear in hub mode. */
@@ -69,6 +70,8 @@
 
     private var shouldOpenWidgetPickerOnStart = false
 
+    private var lockOnDestroy = false
+
     private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
         registerForActivityResult(StartActivityForResult()) { result ->
             when (result.resultCode) {
@@ -149,15 +152,18 @@
     }
 
     private fun onEditDone() {
-        try {
+        lifecycleScope.launch {
             communalViewModel.changeScene(
                 CommunalScenes.Communal,
                 CommunalTransitionKeys.SimpleFade
             )
-            checkNotNull(windowManagerService).lockNow(/* options */ null)
+
+            // Wait for the current scene to be idle on communal.
+            communalViewModel.isIdleOnCommunal.first { it }
+            // Then finish the activity (this helps to avoid a flash of lockscreen when locking
+            // in onDestroy()).
+            lockOnDestroy = true
             finish()
-        } catch (e: RemoteException) {
-            Log.e(TAG, "Couldn't lock the device as WindowManager is dead.")
         }
     }
 
@@ -190,5 +196,15 @@
     override fun onDestroy() {
         super.onDestroy()
         communalViewModel.setEditModeOpen(false)
+
+        if (lockOnDestroy) lockNow()
+    }
+
+    private fun lockNow() {
+        try {
+            checkNotNull(windowManagerService).lockNow(/* options */ null)
+        } catch (e: RemoteException) {
+            Log.e(TAG, "Couldn't lock the device as WindowManager is dead.")
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogActivity.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogActivity.kt
deleted file mode 100644
index 4e40042..0000000
--- a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogActivity.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.contrast
-
-import android.app.Activity
-import android.os.Bundle
-import javax.inject.Inject
-
-/** Trampoline activity responsible for creating a [ContrastDialogDelegate] */
-class ContrastDialogActivity
-@Inject
-constructor(
-    private val contrastDialogDelegate : ContrastDialogDelegate
-) : Activity() {
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        contrastDialogDelegate.createDialog().show()
-        finish()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt
deleted file mode 100644
index 0daa058..0000000
--- a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.contrast
-
-import android.app.UiModeManager
-import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH
-import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM
-import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_STANDARD
-import android.app.UiModeManager.ContrastUtils.fromContrastLevel
-import android.app.UiModeManager.ContrastUtils.toContrastLevel
-import android.os.Bundle
-import android.provider.Settings
-import android.view.View
-import android.widget.FrameLayout
-import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.res.R
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.util.settings.SecureSettings
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-/** Dialog to select contrast options */
-class ContrastDialogDelegate
-@Inject
-constructor(
-    private val sysuiDialogFactory: SystemUIDialog.Factory,
-    @Main private val mainExecutor: Executor,
-    private val uiModeManager: UiModeManager,
-    private val userTracker: UserTracker,
-    private val secureSettings: SecureSettings,
-) : SystemUIDialog.Delegate, UiModeManager.ContrastChangeListener {
-
-    @VisibleForTesting lateinit var contrastButtons: Map<Int, FrameLayout>
-    lateinit var dialogView: View
-    @VisibleForTesting var initialContrast: Float = fromContrastLevel(CONTRAST_LEVEL_STANDARD)
-
-    override fun createDialog(): SystemUIDialog {
-        val dialog = sysuiDialogFactory.create(this)
-        dialogView = dialog.layoutInflater.inflate(R.layout.contrast_dialog, null)
-        with(dialog) {
-            setView(dialogView)
-
-            setTitle(R.string.quick_settings_contrast_label)
-            setNeutralButton(R.string.cancel) { _, _ ->
-                secureSettings.putFloatForUser(
-                    Settings.Secure.CONTRAST_LEVEL,
-                    initialContrast,
-                    userTracker.userId
-                )
-                dialog.dismiss()
-            }
-            setPositiveButton(com.android.settingslib.R.string.done) { _, _ -> dialog.dismiss() }
-        }
-
-        return dialog
-    }
-
-    override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
-        contrastButtons =
-            mapOf(
-                CONTRAST_LEVEL_STANDARD to dialog.requireViewById(R.id.contrast_button_standard),
-                CONTRAST_LEVEL_MEDIUM to dialog.requireViewById(R.id.contrast_button_medium),
-                CONTRAST_LEVEL_HIGH to dialog.requireViewById(R.id.contrast_button_high)
-            )
-
-        contrastButtons.forEach { (contrastLevel, contrastButton) ->
-            contrastButton.setOnClickListener {
-                val contrastValue = fromContrastLevel(contrastLevel)
-                secureSettings.putFloatForUser(
-                    Settings.Secure.CONTRAST_LEVEL,
-                    contrastValue,
-                    userTracker.userId
-                )
-            }
-        }
-
-        initialContrast = uiModeManager.contrast
-        highlightContrast(toContrastLevel(initialContrast))
-    }
-
-    override fun onStart(dialog: SystemUIDialog) {
-        uiModeManager.addContrastChangeListener(mainExecutor, this)
-    }
-
-    override fun onStop(dialog: SystemUIDialog) {
-        uiModeManager.removeContrastChangeListener(this)
-    }
-
-    override fun onContrastChanged(contrast: Float) {
-        highlightContrast(toContrastLevel(contrast))
-    }
-
-    private fun highlightContrast(contrast: Int) {
-        contrastButtons.forEach { (level, button) -> button.isSelected = level == contrast }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index d2df276..c2e1e33 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -20,7 +20,6 @@
 
 import com.android.systemui.ForegroundServicesDialog;
 import com.android.systemui.communal.widgets.EditWidgetsActivity;
-import com.android.systemui.contrast.ContrastDialogActivity;
 import com.android.systemui.keyguard.WorkLockActivity;
 import com.android.systemui.people.PeopleSpaceActivity;
 import com.android.systemui.people.widget.LaunchConversationActivity;
@@ -72,12 +71,6 @@
     @ClassKey(BrightnessDialog.class)
     public abstract Activity bindBrightnessDialog(BrightnessDialog activity);
 
-    /** Inject into ContrastDialogActivity. */
-    @Binds
-    @IntoMap
-    @ClassKey(ContrastDialogActivity.class)
-    public abstract Activity bindContrastDialogActivity(ContrastDialogActivity activity);
-
     /** Inject into UsbDebuggingActivity. */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index 30a56a2..813fccf 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -48,6 +48,7 @@
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions
@@ -302,7 +303,7 @@
 
     private fun listenForSchedulingWatchdog() {
         keyguardTransitionInteractor
-            .transition(to = KeyguardState.GONE)
+            .transition(Edge.create(to = KeyguardState.GONE))
             .filter { it.transitionState == TransitionState.FINISHED }
             .onEach {
                 // We deliberately want to run this in background because scheduleWatchdog does
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index 6c6683a..669cd94 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
@@ -126,9 +127,9 @@
             .launchIn(applicationScope)
 
         merge(
-                keyguardTransitionInteractor.transition(AOD, LOCKSCREEN),
-                keyguardTransitionInteractor.transition(OFF, LOCKSCREEN),
-                keyguardTransitionInteractor.transition(DOZING, LOCKSCREEN),
+                keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN)),
+                keyguardTransitionInteractor.transition(Edge.create(OFF, LOCKSCREEN)),
+                keyguardTransitionInteractor.transition(Edge.create(DOZING, LOCKSCREEN)),
             )
             .filter { it.transitionState == TransitionState.STARTED }
             .sample(powerInteractor.detailedWakefulness)
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 6e04339..1e725eb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -16,15 +16,21 @@
 
 package com.android.systemui.dreams;
 
+import static android.service.dreams.Flags.dreamHandlesBeingObscured;
+
 import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
 import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamAlphaScaledExpansion;
 import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamYPositionScaledExpansion;
+import static com.android.systemui.Flags.communalHub;
+import static com.android.systemui.Flags.glanceableHubGestureHandle;
 import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM;
 import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP;
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
 
 import android.animation.Animator;
+import android.app.DreamManager;
 import android.content.res.Resources;
 import android.graphics.Region;
 import android.os.Handler;
@@ -37,7 +43,9 @@
 import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
 import com.android.systemui.complication.ComplicationHostViewController;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.dreams.dagger.DreamOverlayModule;
@@ -45,10 +53,12 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.res.R;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.util.ViewController;
 
 import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.flow.FlowKt;
 
 import java.util.Arrays;
 
@@ -68,6 +78,8 @@
     private final DreamOverlayStateController mStateController;
     private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    private final ShadeInteractor mShadeInteractor;
+    private final CommunalInteractor mCommunalInteractor;
 
     private final ComplicationHostViewController mComplicationHostViewController;
 
@@ -87,9 +99,10 @@
 
     // Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
     private final Handler mHandler;
-    private final CoroutineDispatcher mMainDispatcher;
+    private final CoroutineDispatcher mBackgroundDispatcher;
     private final int mDreamOverlayMaxTranslationY;
     private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
+    private final DreamManager mDreamManager;
 
     private long mJitterStartTimeMillis;
 
@@ -174,11 +187,12 @@
             DreamOverlayContainerView containerView,
             ComplicationHostViewController complicationHostViewController,
             @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
+            @Named(DreamOverlayModule.HUB_GESTURE_INDICATOR_VIEW) View hubGestureIndicatorView,
             DreamOverlayStatusBarViewController statusBarViewController,
             LowLightTransitionCoordinator lowLightTransitionCoordinator,
             BlurUtils blurUtils,
             @Main Handler handler,
-            @Main CoroutineDispatcher mainDispatcher,
+            @Background CoroutineDispatcher backgroundDispatcher,
             @Main Resources resources,
             @Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
             @Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
@@ -188,22 +202,33 @@
             DreamOverlayAnimationsController animationsController,
             DreamOverlayStateController stateController,
             BouncerlessScrimController bouncerlessScrimController,
-            KeyguardTransitionInteractor keyguardTransitionInteractor) {
+            KeyguardTransitionInteractor keyguardTransitionInteractor,
+            ShadeInteractor shadeInteractor,
+            CommunalInteractor communalInteractor,
+            DreamManager dreamManager) {
         super(containerView);
         mDreamOverlayContentView = contentView;
         mStatusBarViewController = statusBarViewController;
         mBlurUtils = blurUtils;
         mDreamOverlayAnimationsController = animationsController;
         mStateController = stateController;
+        mCommunalInteractor = communalInteractor;
         mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
 
         mBouncerlessScrimController = bouncerlessScrimController;
 
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+        mShadeInteractor = shadeInteractor;
 
         mComplicationHostViewController = complicationHostViewController;
         mDreamOverlayMaxTranslationY = resources.getDimensionPixelSize(
                 R.dimen.dream_overlay_y_offset);
+
+        if (communalHub() && glanceableHubGestureHandle()) {
+            // TODO(b/339667383): remove this temporary swipe gesture handle
+            hubGestureIndicatorView.setVisibility(View.VISIBLE);
+        }
+
         final View view = mComplicationHostViewController.getView();
 
         mDreamOverlayContentView.addView(view,
@@ -211,11 +236,12 @@
                         ViewGroup.LayoutParams.MATCH_PARENT));
 
         mHandler = handler;
-        mMainDispatcher = mainDispatcher;
+        mBackgroundDispatcher = backgroundDispatcher;
         mMaxBurnInOffset = maxBurnInOffset;
         mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
         mMillisUntilFullJitter = millisUntilFullJitter;
         mPrimaryBouncerCallbackInteractor = primaryBouncerCallbackInteractor;
+        mDreamManager = dreamManager;
     }
 
     @Override
@@ -238,11 +264,21 @@
         mView.getRootSurfaceControl().setTouchableRegion(emptyRegion);
         emptyRegion.recycle();
 
-        collectFlow(
-                mView,
-                mKeyguardTransitionInteractor.isFinishedInStateWhere(KeyguardState::isBouncerState),
-                isFinished -> mAnyBouncerShowing = isFinished,
-                mMainDispatcher);
+        if (dreamHandlesBeingObscured()) {
+            collectFlow(
+                    mView,
+                    FlowKt.distinctUntilChanged(combineFlows(
+                            mKeyguardTransitionInteractor.isFinishedInStateWhere(
+                                    KeyguardState::isBouncerState),
+                            mShadeInteractor.isAnyExpanded(),
+                            mCommunalInteractor.isCommunalShowing(),
+                            (anyBouncerShowing, shadeExpanded, communalShowing) -> {
+                                mAnyBouncerShowing = anyBouncerShowing;
+                                return anyBouncerShowing || shadeExpanded || communalShowing;
+                            })),
+                    mDreamManager::setDreamIsObscured,
+                    mBackgroundDispatcher);
+        }
 
         // Start dream entry animations. Skip animations for low light clock.
         if (!mStateController.isLowLightActive()) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 999e681..789b7f8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -18,6 +18,7 @@
 
 import android.content.res.Resources;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.lifecycle.Lifecycle;
@@ -39,6 +40,7 @@
 @Module
 public abstract class DreamOverlayModule {
     public static final String DREAM_OVERLAY_CONTENT_VIEW = "dream_overlay_content_view";
+    public static final String HUB_GESTURE_INDICATOR_VIEW = "hub_gesture_indicator_view";
     public static final String MAX_BURN_IN_OFFSET = "max_burn_in_offset";
     public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL =
             "burn_in_protection_update_interval";
@@ -71,6 +73,18 @@
                 "R.id.dream_overlay_content must not be null");
     }
 
+    /**
+     * Gesture indicator bar on the right edge of the screen to indicate to users that they can
+     * swipe to see their widgets on lock screen.
+     */
+    @Provides
+    @DreamOverlayComponent.DreamOverlayScope
+    @Named(HUB_GESTURE_INDICATOR_VIEW)
+    public static View providesHubGestureIndicatorView(DreamOverlayContainerView view) {
+        return Preconditions.checkNotNull(view.findViewById(R.id.glanceable_hub_handle),
+                "R.id.glanceable_hub_handle must not be null");
+    }
+
     /** */
     @Provides
     public static TouchInsetManager.TouchInsetSession providesTouchInsetSession(
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index fff0c58..1c047dd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -98,7 +98,7 @@
         // Notification shade window has its own logic to be visible if the hub is open, no need to
         // do anything here other than send touch events over.
         session.registerInputListener(ev -> {
-            surfaces.handleExternalShadeWindowTouch((MotionEvent) ev);
+            surfaces.handleDreamTouch((MotionEvent) ev);
             if (ev != null && ((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
                 var unused = session.pop();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index 221f790..c5b3c53 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel
@@ -97,7 +98,7 @@
             .distinctUntilChanged()
 
     val transitionEnded =
-        keyguardTransitionInteractor.transition(from = DREAMING).filter { step ->
+        keyguardTransitionInteractor.transition(Edge.create(from = DREAMING)).filter { step ->
             step.transitionState == TransitionState.FINISHED ||
                 step.transitionState == TransitionState.CANCELED
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
index 9876fe4..f04cbb8 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
@@ -477,7 +477,7 @@
         }
 
         private inline fun PrintWriter.wrapSection(entry: DumpsysEntry, block: () -> Unit) {
-            Trace.beginSection(entry.name)
+            Trace.beginSection(entry.name.take(Trace.MAX_SECTION_NAME_LEN))
             preamble(entry)
             val dumpTime = measureTimeMillis(block)
             footer(entry, dumpTime)
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 67c5564..1404340 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -29,11 +29,13 @@
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.shared.ComposeLockscreen
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
 import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression
 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
 import javax.inject.Inject
 
 /** A class in which engineers can define flag dependencies */
@@ -49,6 +51,7 @@
         NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token
         FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token
         NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token
+        PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token
 
         // SceneContainer dependencies
         SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 95bc514..49be03c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -2739,12 +2739,10 @@
         protected final void setRotationSuggestionsEnabled(boolean enabled) {
             try {
                 final int userId = Binder.getCallingUserHandle().getIdentifier();
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                if (enabled) {
-                    info.setRotationSuggestionDisabled(true);
-                }
-                mStatusBarService.disableForUser(info, mToken, mContext.getPackageName(), userId,
-                        "setRotationSuggestionsEnabled");
+                final int what = enabled
+                        ? StatusBarManager.DISABLE2_NONE
+                        : StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
+                mStatusBarService.disable2ForUser(what, mToken, mContext.getPackageName(), userId);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
index d3f7e24..44f1c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
@@ -17,19 +17,43 @@
 package com.android.systemui.keyboard.shortcut.domain.interactor
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperRepository
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
+import com.android.systemui.model.SysUiState
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shared.system.QuickStepContract
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
 
 @SysUISingleton
 class ShortcutHelperInteractor
 @Inject
-constructor(private val repository: ShortcutHelperRepository) {
+constructor(
+    private val displayTracker: DisplayTracker,
+    @Background private val backgroundScope: CoroutineScope,
+    private val sysUiState: SysUiState,
+    private val repository: ShortcutHelperRepository
+) {
 
     val state: Flow<ShortcutHelperState> = repository.state
 
-    fun onUserLeave() {
+    fun onViewClosed() {
         repository.hide()
+        setSysUiStateFlagEnabled(false)
+    }
+
+    fun onViewOpened() {
+        setSysUiStateFlagEnabled(true)
+    }
+
+    private fun setSysUiStateFlagEnabled(enabled: Boolean) {
+        backgroundScope.launch {
+            sysUiState
+                .setFlag(QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING, enabled)
+                .commitUpdate(displayTracker.defaultDisplayId)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
index 934f9ee..ef4156d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
@@ -63,12 +63,13 @@
         setUpSheetDismissListener()
         setUpDismissOnTouchOutside()
         observeFinishRequired()
+        viewModel.onViewOpened()
     }
 
     override fun onDestroy() {
         super.onDestroy()
         if (isFinishing) {
-            viewModel.onUserLeave()
+            viewModel.onViewClosed()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index 7e48c65..c623f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -38,7 +38,11 @@
             .distinctUntilChanged()
             .flowOn(backgroundDispatcher)
 
-    fun onUserLeave() {
-        interactor.onUserLeave()
+    fun onViewClosed() {
+        interactor.onViewClosed()
+    }
+
+    fun onViewOpened() {
+        interactor.onViewOpened()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index dbaa297..68a252b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -37,6 +37,7 @@
 import android.service.notification.ZenModeConfig;
 import android.text.TextUtils;
 import android.text.style.StyleSpan;
+import android.util.Log;
 
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -212,21 +213,27 @@
     @AnyThread
     @Override
     public Slice onBindSlice(Uri sliceUri) {
-        Trace.beginSection("KeyguardSliceProvider#onBindSlice");
-        Slice slice;
-        synchronized (this) {
-            ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
-            if (needsMediaLocked()) {
-                addMediaLocked(builder);
-            } else {
-                builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
+        Slice slice = null;
+        try {
+            Trace.beginSection("KeyguardSliceProvider#onBindSlice");
+            synchronized (this) {
+                ListBuilder builder = new ListBuilder(getContext(), mSliceUri,
+                        ListBuilder.INFINITY);
+                if (needsMediaLocked()) {
+                    addMediaLocked(builder);
+                } else {
+                    builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
+                }
+                addNextAlarmLocked(builder);
+                addZenModeLocked(builder);
+                addPrimaryActionLocked(builder);
+                slice = builder.build();
             }
-            addNextAlarmLocked(builder);
-            addZenModeLocked(builder);
-            addPrimaryActionLocked(builder);
-            slice = builder.build();
+        } catch (IllegalStateException e) {
+            Log.w(TAG, "Could not initialize slice", e);
+        } finally {
+            Trace.endSection();
         }
-        Trace.endSection();
         return slice;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d6fd354..81c2d92 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.keyguard;
 
-import static android.app.StatusBarManager.DISABLE2_NONE;
 import static android.app.StatusBarManager.SESSION_KEYGUARD;
 import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT;
 import static android.provider.Settings.System.LOCKSCREEN_SOUNDS_ENABLED;
@@ -1077,6 +1076,33 @@
         }
     };
 
+    /**
+     * For now, the keyguard-appearing animation is a no-op, because we assume that this is
+     * happening while the screen is already off or turning off.
+     *
+     * TODO(b/278086361): create an animation for keyguard appearing over a non-showWhenLocked
+     * activity.
+     */
+    private final IRemoteAnimationRunner.Stub mAppearAnimationRunner =
+            new IRemoteAnimationRunner.Stub() {
+        @Override
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] apps,
+                RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
+                IRemoteAnimationFinishedCallback finishedCallback) {
+            try {
+                finishedCallback.onAnimationFinished();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to finish transition", e);
+            }
+        }
+
+        @Override
+        public void onAnimationCancelled() {
+        }
+    };
+
     private final IRemoteAnimationRunner mOccludeAnimationRunner =
             new OccludeActivityLaunchRemoteAnimationRunner(mOccludeAnimationController);
 
@@ -1165,7 +1191,7 @@
                                     finishedCallback.onAnimationFinished();
                                     mOccludeByDreamAnimator = null;
                                 } catch (RemoteException e) {
-                                    e.printStackTrace();
+                                    Log.e(TAG, "Failed to finish transition", e);
                                 }
                             }
                         });
@@ -1280,7 +1306,7 @@
 
                                     mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION);
                                 } catch (RemoteException e) {
-                                    e.printStackTrace();
+                                    Log.e(TAG, "Failed to finish transition", e);
                                 }
                             }
                         });
@@ -1546,6 +1572,7 @@
 
         mKeyguardTransitions.register(
                 KeyguardService.wrap(this, getExitAnimationRunner()),
+                KeyguardService.wrap(this, getAppearAnimationRunner()),
                 KeyguardService.wrap(this, getOccludeAnimationRunner()),
                 KeyguardService.wrap(this, getOccludeByDreamAnimationRunner()),
                 KeyguardService.wrap(this, getUnoccludeAnimationRunner()));
@@ -2124,6 +2151,10 @@
         return validatingRemoteAnimationRunner(mExitAnimationRunner);
     }
 
+    public IRemoteAnimationRunner getAppearAnimationRunner() {
+        return validatingRemoteAnimationRunner(mAppearAnimationRunner);
+    }
+
     public IRemoteAnimationRunner getOccludeAnimationRunner() {
         if (KeyguardWmStateRefactor.isEnabled()) {
             return validatingRemoteAnimationRunner(mWmOcclusionManager.getOccludeAnimationRunner());
@@ -3357,7 +3388,7 @@
             }
         } catch (RemoteException e) {
             mSurfaceBehindRemoteAnimationRequested = false;
-            e.printStackTrace();
+            Log.e(TAG, "Failed to report keyguardGoingAway", e);
         }
     }
 
@@ -3440,12 +3471,9 @@
             //  unless disable is called to show un-hide it once first
             if (forceClearFlags) {
                 try {
-                    StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo(flags,
-                            DISABLE2_NONE);
-                    mStatusBarService.disableForUser(info, mStatusBarDisableToken,
+                    mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
                             mContext.getPackageName(),
-                            mSelectedUserInteractor.getSelectedUserId(true),
-                            "adjustStatusBarLocked - force clear flags");
+                            mSelectedUserInteractor.getSelectedUserId(true));
                 } catch (RemoteException e) {
                     Log.d(TAG, "Failed to force clear flags", e);
                 }
@@ -3471,11 +3499,9 @@
             }
 
             try {
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo(flags,
-                        DISABLE2_NONE);
-                mStatusBarService.disableForUser(info, mStatusBarDisableToken,
-                        mContext.getPackageName(), mSelectedUserInteractor.getSelectedUserId(true),
-                        "adjustStatusBarLocked - set disable flags");
+                mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
+                        mContext.getPackageName(),
+                        mSelectedUserInteractor.getSelectedUserId(true));
             } catch (RemoteException e) {
                 Log.d(TAG, "Failed to set disable flags: " + flags, e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index a65a882..3cbcb2c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -29,15 +29,20 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.utils.GlobalWindowManager
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 
@@ -59,6 +64,7 @@
     @Application private val applicationScope: CoroutineScope,
     @Background private val bgDispatcher: CoroutineDispatcher,
     private val featureFlags: FeatureFlags,
+    private val sceneInteractor: SceneInteractor,
 ) : CoreStartable, WakefulnessLifecycle.Observer {
 
     override fun start() {
@@ -84,9 +90,15 @@
 
         applicationScope.launch(bgDispatcher) {
             // We drop 1 to avoid triggering on initial collect().
-            keyguardTransitionInteractor.transition(to = GONE).collect { transition ->
-                if (transition.transitionState == TransitionState.FINISHED) {
-                    onKeyguardGone()
+            if (SceneContainerFlag.isEnabled) {
+                sceneInteractor.transitionState
+                    .filter { it.isIdle(Scenes.Gone) }
+                    .collect { onKeyguardGone() }
+            } else {
+                keyguardTransitionInteractor.transition(Edge.create(to = GONE)).collect {
+                    if (it.transitionState == TransitionState.FINISHED) {
+                        onKeyguardGone()
+                    }
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 00f5002..1b342ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -23,6 +23,7 @@
 import android.view.WindowManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import java.util.concurrent.Executor
@@ -40,6 +41,7 @@
     private val activityTaskManagerService: IActivityTaskManager,
     private val keyguardStateController: KeyguardStateController,
     private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
 ) {
 
     /**
@@ -141,6 +143,14 @@
         finishedCallback: IRemoteAnimationFinishedCallback
     ) {
         if (apps.isNotEmpty()) {
+            // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
+            // going away animation on its own, if an activity launches and then requests dismissing
+            // the keyguard. In this case, this is the first and only signal we'll receive to start
+            // a transition to GONE.
+            keyguardTransitionInteractor.startDismissKeyguardTransition(
+                reason = "Going away remote animation started"
+            )
+
             goingAwayRemoteAnimationFinishedCallback = finishedCallback
             keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 7f3274c..7655d7a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -247,7 +247,7 @@
         state: TransitionState
     ) {
         if (updateTransitionId != transitionId) {
-            Log.wtf(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
+            Log.w(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
             return
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt
new file mode 100644
index 0000000..80bdc65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+@SysUISingleton
+class LockscreenSceneTransitionRepository @Inject constructor() {
+
+    /**
+     * This [KeyguardState] will indicate which sub state within KTF should be navigated to when the
+     * next transition into the Lockscreen scene is started. It will be consumed exactly once and
+     * after that the state will be set back to [DEFAULT_STATE].
+     */
+    val nextLockscreenTargetState: MutableStateFlow<KeyguardState> = MutableStateFlow(DEFAULT_STATE)
+
+    companion object {
+        val DEFAULT_STATE = KeyguardState.LOCKSCREEN
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
index eef4b97..9626077 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.content.Context
+import android.util.Log
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
@@ -39,6 +40,7 @@
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 
 /**
@@ -96,10 +98,13 @@
                     keyguardUpdateMonitor.isFingerprintDetectionRunning &&
                     keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed
             }
+            .onEach { Log.d(TAG, "showIndicatorForPrimaryBouncer updated: $it") }
 
     private val showIndicatorForAlternateBouncer: Flow<Boolean> =
         // Note: this interactor internally verifies that SideFPS is enabled and running.
-        alternateBouncerInteractor.isVisible
+        alternateBouncerInteractor.isVisible.onEach {
+            Log.d(TAG, "showIndicatorForAlternateBouncer updated: $it")
+        }
 
     /**
      * Indicates whether the primary or alternate bouncers request showing the side fingerprint
@@ -112,6 +117,7 @@
                 showForPrimaryBouncer || showForAlternateBouncer
             }
             .distinctUntilChanged()
+            .onEach { Log.d(TAG, "showIndicatorForDeviceEntry updated: $it") }
 
     private fun isBouncerActive(): Boolean {
         if (SceneContainerFlag.isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 5a28f711..9b07675f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
 import com.android.wm.shell.animation.Interpolators
 import javax.inject.Inject
@@ -140,6 +141,8 @@
     }
 
     private fun listenForAlternateBouncerToGone() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // Handled via #dismissAlternateBouncer.
             return
@@ -162,6 +165,8 @@
     }
 
     private fun listenForAlternateBouncerToPrimaryBouncer() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
                 .filterRelevantKeyguardStateAnd { isPrimaryBouncerShowing ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 4d73774..a306954 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.Utils.Companion.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -185,6 +186,7 @@
      * PRIMARY_BOUNCER.
      */
     private fun listenForAodToPrimaryBouncer() {
+        if (SceneContainerFlag.isEnabled) return
         scope.launch("$TAG#listenForAodToPrimaryBouncer") {
             keyguardInteractor.primaryBouncerShowing
                 .filterRelevantKeyguardStateAnd { primaryBouncerShowing -> primaryBouncerShowing }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
index e738ea4..63294f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -93,6 +94,8 @@
     }
 
     private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
                 .filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing }
@@ -101,6 +104,8 @@
     }
 
     private fun listenForDreamingLockscreenHostedToGone() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.biometricUnlockState
                 .filterRelevantKeyguardStateAnd { biometricUnlockState ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index c952e08..7961b45 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
@@ -88,6 +89,8 @@
 
     private fun listenForDreamingToGlanceableHub() {
         if (!communalHub()) return
+        if (SceneContainerFlag.isEnabled)
+            return // TODO(b/336576536): Check if adaptation for scene framework is needed
         scope.launch("$TAG#listenForDreamingToGlanceableHub", mainDispatcher) {
             glanceableHubTransitions.listenForGlanceableHubTransition(
                 transitionOwnerName = TAG,
@@ -175,6 +178,8 @@
     }
 
     private fun listenForDreamingToGoneWhenDismissable() {
+        if (SceneContainerFlag.isEnabled)
+            return // TODO(b/336576536): Check if adaptation for scene framework is needed
         scope.launch {
             keyguardInteractor.isAbleToDream
                 .sampleCombine(
@@ -190,6 +195,8 @@
     }
 
     private fun listenForDreamingToGoneFromBiometricUnlock() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.biometricUnlockState
                 .filterRelevantKeyguardStateAnd { biometricUnlockState ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index faab033..ca6ab3e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -28,9 +28,11 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -62,6 +64,8 @@
     ) {
 
     override fun start() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         if (!Flags.communalHub()) {
             return
         }
@@ -79,6 +83,7 @@
             duration =
                 when (toState) {
                     KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+                    KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
                     else -> DEFAULT_DURATION
                 }.inWholeMilliseconds
         }
@@ -171,5 +176,6 @@
         const val TAG = "FromGlanceableHubTransitionInteractor"
         val DEFAULT_DURATION = 1.seconds
         val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
+        val TO_OCCLUDED_DURATION = 450.milliseconds
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index c2c095b..2b3732f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -62,6 +63,8 @@
     ) {
 
     override fun start() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         listenForGoneToAodOrDozing()
         listenForGoneToDreaming()
         listenForGoneToLockscreenOrHub()
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 56261e0..f1e98f3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -20,6 +20,7 @@
 import android.util.MathUtils
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -32,6 +33,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
 import java.util.UUID
@@ -150,6 +152,7 @@
     }
 
     private fun listenForLockscreenToPrimaryBouncer() {
+        if (SceneContainerFlag.isEnabled) return
         scope.launch("$TAG#listenForLockscreenToPrimaryBouncer") {
             keyguardInteractor.primaryBouncerShowing
                 .filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing }
@@ -174,6 +177,7 @@
 
     /* Starts transitions when manually dragging up the bouncer from the lockscreen. */
     private fun listenForLockscreenToPrimaryBouncerDragging() {
+        if (SceneContainerFlag.isEnabled) return
         var transitionId: UUID? = null
         scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") {
             shadeRepository.legacyShadeExpansion
@@ -259,7 +263,9 @@
     }
 
     fun dismissKeyguard() {
-        scope.launch("$TAG#dismissKeyguard") { startTransitionTo(KeyguardState.GONE) }
+        scope.launch("$TAG#dismissKeyguard") {
+            startTransitionTo(KeyguardState.GONE, ownerReason = "#dismissKeyguard()")
+        }
     }
 
     private fun listenForLockscreenToGone() {
@@ -280,6 +286,7 @@
     }
 
     private fun listenForLockscreenToGoneDragging() {
+        if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // When the refactor is enabled, we no longer use isKeyguardGoingAway.
             scope.launch("$TAG#listenForLockscreenToGoneDragging") {
@@ -337,7 +344,9 @@
      * keyguard transition.
      */
     private fun listenForLockscreenToGlanceableHub() {
-        if (!com.android.systemui.Flags.communalHub()) {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
+        if (!Flags.communalHub()) {
             return
         }
         scope.launch(mainDispatcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 2a7178f..2603aab2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.Utils.Companion.sample
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
@@ -91,20 +92,14 @@
                     .sample(
                         communalInteractor.isIdleOnCommunal,
                         communalInteractor.showCommunalFromOccluded,
-                        communalInteractor.dreamFromOccluded
+                        communalInteractor.dreamFromOccluded,
                     )
                     .collect { (_, isIdleOnCommunal, showCommunalFromOccluded, dreamFromOccluded) ->
-                        // Occlusion signals come from the framework, and should interrupt any
-                        // existing transition
-                        val to =
-                            if (restartDreamOnUnocclude() && dreamFromOccluded) {
-                                KeyguardState.DREAMING
-                            } else if (isIdleOnCommunal || showCommunalFromOccluded) {
-                                KeyguardState.GLANCEABLE_HUB
-                            } else {
-                                KeyguardState.LOCKSCREEN
-                            }
-                        startTransitionTo(to)
+                        startTransitionToLockscreenOrHub(
+                            isIdleOnCommunal,
+                            showCommunalFromOccluded,
+                            dreamFromOccluded
+                        )
                     }
             }
         } else {
@@ -121,23 +116,35 @@
                     }
                     .collect { (_, _, isIdleOnCommunal, showCommunalFromOccluded, dreamFromOccluded)
                         ->
-                        // Occlusion signals come from the framework, and should interrupt any
-                        // existing transition
-                        val to =
-                            if (restartDreamOnUnocclude() && dreamFromOccluded) {
-                                KeyguardState.DREAMING
-                            } else if (isIdleOnCommunal || showCommunalFromOccluded) {
-                                KeyguardState.GLANCEABLE_HUB
-                            } else {
-                                KeyguardState.LOCKSCREEN
-                            }
-                        startTransitionTo(to)
+                        startTransitionToLockscreenOrHub(
+                            isIdleOnCommunal,
+                            showCommunalFromOccluded,
+                            dreamFromOccluded
+                        )
                     }
             }
         }
     }
 
+    private suspend fun FromOccludedTransitionInteractor.startTransitionToLockscreenOrHub(
+        isIdleOnCommunal: Boolean,
+        showCommunalFromOccluded: Boolean,
+        dreamFromOccluded: Boolean,
+    ) {
+        if (restartDreamOnUnocclude() && dreamFromOccluded) {
+            startTransitionTo(KeyguardState.DREAMING)
+        } else if (isIdleOnCommunal || showCommunalFromOccluded) {
+            // TODO(b/336576536): Check if adaptation for scene framework is needed
+            if (SceneContainerFlag.isEnabled) return
+            startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+        } else {
+            startTransitionTo(KeyguardState.LOCKSCREEN)
+        }
+    }
+
     private fun listenForOccludedToGone() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // We don't think OCCLUDED to GONE is possible. You should always have to go via a
             // *_BOUNCER state to end up GONE. Launching an activity over a dismissable keyguard
@@ -158,10 +165,6 @@
         }
     }
 
-    fun dismissToGone() {
-        scope.launch { startTransitionTo(KeyguardState.GONE) }
-    }
-
     private fun listenForOccludedToAsleep() {
         scope.launch { listenForSleepTransition() }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 181a551..53a0c32 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.sample
 import com.android.systemui.util.kotlin.sample
@@ -98,6 +99,8 @@
     }
 
     private fun listenForPrimaryBouncerToLockscreenHubOrOccluded() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             scope.launch {
                 keyguardInteractor.primaryBouncerShowing
@@ -158,10 +161,14 @@
     }
 
     private fun listenForPrimaryBouncerToAsleep() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch { listenForSleepTransition() }
     }
 
     private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
                 .sample(keyguardInteractor.isActiveDreamLockscreenHosted, ::Pair)
@@ -174,6 +181,8 @@
     }
 
     private fun listenForPrimaryBouncerToGone() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // This is handled in KeyguardSecurityContainerController and
             // StatusBarKeyguardViewManager, which calls the transition interactor to kick off a
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
index 197221a..fcf67d5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.sample
 import java.util.UUID
 import javax.inject.Inject
@@ -49,6 +50,8 @@
         fromState: KeyguardState,
         toState: KeyguardState,
     ) {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         val toScene =
             if (fromState == KeyguardState.GLANCEABLE_HUB) {
                 CommunalScenes.Blank
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index 857096e..b1ef76e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -20,6 +20,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.content.Context
+import android.util.Log
 import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -42,6 +43,7 @@
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 
 @SysUISingleton
@@ -78,7 +80,15 @@
     private val refreshEvents: Flow<Unit> =
         merge(
             configurationInteractor.onAnyConfigurationChange,
-            fingerprintPropertyInteractor.propertiesInitialized.filter { it }.map { Unit },
+            fingerprintPropertyInteractor.propertiesInitialized
+                .filter { it }
+                .map { Unit }
+                .onEach {
+                    Log.d(
+                        "KeyguardBlueprintInteractor",
+                        "triggering refreshEvent from fpPropertiesInitialized"
+                    )
+                },
         )
 
     init {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
index 08d29d4..1aac1c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
@@ -25,6 +25,9 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -48,6 +51,7 @@
     transitionInteractor: KeyguardTransitionInteractor,
     val dismissInteractor: KeyguardDismissInteractor,
     @Application private val applicationScope: CoroutineScope,
+    sceneInteractor: SceneInteractor,
 ) {
     val dismissAction: Flow<DismissAction> = repository.dismissAction
 
@@ -72,7 +76,12 @@
             )
 
     private val finishedTransitionToGone: Flow<Unit> =
-        transitionInteractor.finishedKeyguardState.filter { it == GONE }.map {} // map to Unit
+        if (SceneContainerFlag.isEnabled) {
+            sceneInteractor.transitionState.filter { it.isIdle(Scenes.Gone) }.map {}
+        } else {
+            transitionInteractor.finishedKeyguardState.filter { it == GONE }.map {}
+        }
+
     val executeDismissAction: Flow<() -> KeyguardDone> =
         merge(
                 finishedTransitionToGone,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 2d7b737..c44a40f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -55,6 +55,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -97,6 +98,7 @@
 
     /** Bounds of the notification container. */
     val notificationContainerBounds: StateFlow<NotificationContainerBounds> by lazy {
+        SceneContainerFlag.assertInLegacyMode()
         combine(
                 _notificationPlaceholderBounds,
                 sharedNotificationContainerInteractor.get().configurationBasedDimensions,
@@ -115,6 +117,7 @@
     }
 
     fun setNotificationContainerBounds(position: NotificationContainerBounds) {
+        SceneContainerFlag.assertInLegacyMode()
         _notificationPlaceholderBounds.value = position
     }
 
@@ -179,7 +182,11 @@
             }
             .sample(powerInteractor.isAwake) { isAbleToDream, isAwake -> isAbleToDream && isAwake }
             .debounce(50L)
-            .distinctUntilChanged()
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false,
+            )
 
     /** Whether the keyguard is showing or not. */
     @Deprecated("Use KeyguardTransitionInteractor + KeyguardState")
@@ -223,7 +230,19 @@
     @JvmField val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerShow
 
     /** Whether the alternate bouncer is showing or not. */
-    val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
+    val alternateBouncerShowing: Flow<Boolean> =
+        bouncerRepository.alternateBouncerVisible.sample(isAbleToDream) {
+            alternateBouncerVisible,
+            isAbleToDream ->
+            if (isAbleToDream) {
+                // If the alternate bouncer will show over a dream, it is likely that the dream has
+                // requested a dismissal, which will stop the dream. By delaying this slightly, the
+                // DREAMING->LOCKSCREEN transition will now happen first, followed by
+                // LOCKSCREEN->ALTERNATE_BOUNCER.
+                delay(600L)
+            }
+            alternateBouncerVisible
+        }
 
     /** Observable for the [StatusBarState] */
     val statusBarState: Flow<StatusBarState> = repository.statusBarState
@@ -299,10 +318,12 @@
                     shadeRepository.legacyShadeExpansion.onStart { emit(0f) },
                     keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(0f) },
                 ) { legacyShadeExpansion, goneValue ->
-                    if (goneValue == 1f || (goneValue == 0f && legacyShadeExpansion == 0f)) {
+                    val isLegacyShadeInResetPosition =
+                        legacyShadeExpansion == 0f || legacyShadeExpansion == 1f
+                    if (goneValue == 1f || (goneValue == 0f && isLegacyShadeInResetPosition)) {
                         // Reset the translation value
                         emit(0f)
-                    } else if (legacyShadeExpansion > 0f && legacyShadeExpansion < 1f) {
+                    } else if (!isLegacyShadeInResetPosition) {
                         // On swipe up, translate the keyguard to reveal the bouncer, OR a GONE
                         // transition is running, which means this is a swipe to dismiss. Values of
                         // 0f and 1f need to be ignored in the legacy shade expansion. These can
@@ -320,7 +341,11 @@
                     }
                 }
             }
-            .distinctUntilChanged()
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = 0f,
+            )
 
     val clockShouldBeCentered: Flow<Boolean> = repository.clockShouldBeCentered
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index e711edc..75c4d6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -19,6 +19,7 @@
 import com.android.keyguard.logging.KeyguardLogger
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.log.core.LogLevel.VERBOSE
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -26,6 +27,7 @@
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.debounce
 import kotlinx.coroutines.launch
 
 private val TAG = KeyguardTransitionAuditLogger::class.simpleName!!
@@ -41,6 +43,7 @@
     private val logger: KeyguardLogger,
     private val powerInteractor: PowerInteractor,
     private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+    private val keyguardRootViewModel: KeyguardRootViewModel,
     private val shadeInteractor: ShadeInteractor,
     private val keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
 ) {
@@ -72,8 +75,8 @@
 
         if (!SceneContainerFlag.isEnabled) {
             scope.launch {
-                sharedNotificationContainerViewModel.bounds.collect {
-                    logger.log(TAG, VERBOSE, "Notif: bounds", it)
+                sharedNotificationContainerViewModel.bounds.debounce(20L).collect {
+                    logger.log(TAG, VERBOSE, "Notif: bounds (debounced)", it)
                 }
             }
         }
@@ -113,6 +116,18 @@
         }
 
         scope.launch {
+            keyguardInteractor.keyguardTranslationY.collect {
+                logger.log(TAG, VERBOSE, "keyguardTranslationY", it)
+            }
+        }
+
+        scope.launch {
+            keyguardRootViewModel.burnInModel.debounce(20L).collect {
+                logger.log(TAG, VERBOSE, "BurnInModel (debounced)", it)
+            }
+        }
+
+        scope.launch {
             keyguardInteractor.isKeyguardDismissible.collect {
                 logger.log(TAG, VERBOSE, "isDismissible", it)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index a18579d..c65dc30 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -17,7 +17,10 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.annotation.FloatRange
+import android.annotation.SuppressLint
 import android.util.Log
+import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -28,12 +31,16 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.util.kotlin.pairwise
+import java.util.UUID
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -68,14 +75,17 @@
     private val fromAlternateBouncerTransitionInteractor:
         dagger.Lazy<FromAlternateBouncerTransitionInteractor>,
     private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>,
+    private val sceneInteractor: dagger.Lazy<SceneInteractor>,
 ) {
-    private val transitionMap = mutableMapOf<Edge, MutableSharedFlow<TransitionStep>>()
+    private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>()
 
     /**
      * Numerous flows are derived from, or care directly about, the transition value in and out of a
      * single state. This prevent the redundant filters from running.
      */
     private val transitionValueCache = mutableMapOf<KeyguardState, MutableSharedFlow<Float>>()
+
+    @SuppressLint("SharedFlowCreation")
     private fun getTransitionValueFlow(state: KeyguardState): MutableSharedFlow<Float> {
         return transitionValueCache.getOrPut(state) {
             MutableSharedFlow<Float>(
@@ -90,6 +100,9 @@
     @Deprecated("Not performant - Use something else in this class")
     val transitions = repository.transitions
 
+    val transitionState: StateFlow<TransitionStep> =
+        transitions.stateIn(scope, SharingStarted.Eagerly, TransitionStep())
+
     /**
      * A pair of the most recent STARTED step, and the transition step immediately preceding it. The
      * transition framework enforces that the previous step is either a CANCELED or FINISHED step,
@@ -99,6 +112,7 @@
      * FINISHED. In the case of a CANCELED step, we can also figure out which state we were coming
      * from when we were canceled.
      */
+    @SuppressLint("SharedFlowCreation")
     val startedStepWithPrecedingStep =
         repository.transitions
             .pairwise()
@@ -119,11 +133,11 @@
         scope.launch {
             repository.transitions.collect {
                 // FROM->TO
-                transitionMap[Edge(it.from, it.to)]?.emit(it)
+                transitionMap[Edge.create(it.from, it.to)]?.emit(it)
                 // FROM->(ANY)
-                transitionMap[Edge(it.from, null)]?.emit(it)
+                transitionMap[Edge.create(it.from, null)]?.emit(it)
                 // (ANY)->TO
-                transitionMap[Edge(null, it.to)]?.emit(it)
+                transitionMap[Edge.create(null, it.to)]?.emit(it)
             }
         }
 
@@ -143,25 +157,70 @@
         }
     }
 
-    /** Given an [edge], return a SharedFlow to collect only relevant [TransitionStep]. */
-    fun getOrCreateFlow(edge: Edge): MutableSharedFlow<TransitionStep> {
-        return transitionMap.getOrPut(edge) {
-            MutableSharedFlow<TransitionStep>(
-                extraBufferCapacity = 10,
-                onBufferOverflow = BufferOverflow.DROP_OLDEST
-            )
+    fun transition(edge: Edge, edgeWithoutSceneContainer: Edge): Flow<TransitionStep> {
+        return transition(if (SceneContainerFlag.isEnabled) edge else edgeWithoutSceneContainer)
+    }
+
+    /** Given an [edge], return a Flow to collect only relevant [TransitionStep]s. */
+    @SuppressLint("SharedFlowCreation")
+    fun transition(edge: Edge): Flow<TransitionStep> {
+        edge.verifyValidKeyguardStates()
+        val mappedEdge = getMappedEdge(edge)
+
+        val flow: Flow<TransitionStep> =
+            transitionMap.getOrPut(mappedEdge) {
+                MutableSharedFlow(
+                    extraBufferCapacity = 10,
+                    onBufferOverflow = BufferOverflow.DROP_OLDEST
+                )
+            }
+
+        return if (SceneContainerFlag.isEnabled) {
+            flow.filter {
+                val fromScene =
+                    when (edge) {
+                        is Edge.StateToState -> edge.from?.mapToSceneContainerScene()
+                        is Edge.StateToScene -> edge.from.mapToSceneContainerScene()
+                        is Edge.SceneToState -> edge.from
+                    }
+
+                val toScene =
+                    when (edge) {
+                        is Edge.StateToState -> edge.to?.mapToSceneContainerScene()
+                        is Edge.StateToScene -> edge.to
+                        is Edge.SceneToState -> edge.to.mapToSceneContainerScene()
+                    }
+
+                fun SceneKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null
+
+                return@filter (fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull()) ||
+                    sceneInteractor.get().transitionState.value.isTransitioning(fromScene, toScene)
+            }
+        } else {
+            flow
         }
     }
 
     /**
-     * Receive all [TransitionStep] matching a filter of [from]->[to]. Allow nulls in order to match
-     * any transition, for instance (any)->GONE.
+     * Converts old KTF states to UNDEFINED when [SceneContainerFlag] is enabled.
+     *
+     * Does nothing otherwise.
+     *
+     * This method should eventually be removed when new code is only written for scene container.
+     * Even when all edges are ported today, there is still development on going in production that
+     * might utilize old states.
      */
-    fun transition(from: KeyguardState? = null, to: KeyguardState? = null): Flow<TransitionStep> {
-        if (from == null && to == null) {
-            throw IllegalArgumentException("from and to cannot both be null")
+    private fun getMappedEdge(edge: Edge): Edge.StateToState {
+        if (!SceneContainerFlag.isEnabled) return edge as Edge.StateToState
+        return when (edge) {
+            is Edge.StateToState ->
+                Edge.create(
+                    from = edge.from?.mapToSceneContainerState(),
+                    to = edge.to?.mapToSceneContainerState()
+                )
+            is Edge.SceneToState -> Edge.create(UNDEFINED, edge.to)
+            is Edge.StateToScene -> Edge.create(edge.from, UNDEFINED)
         }
-        return getOrCreateFlow(Edge(from = from, to = to))
     }
 
     /**
@@ -180,6 +239,7 @@
      * AOD<->* transition information, mapped to dozeAmount range of AOD (1f) <->
      * * (0f).
      */
+    @SuppressLint("SharedFlowCreation")
     val dozeAmountTransition: Flow<TransitionStep> =
         repository.transitions
             .filter { step -> step.from == AOD || step.to == AOD }
@@ -201,11 +261,22 @@
         repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
 
     /** The destination state of the last [TransitionState.STARTED] transition. */
+    @SuppressLint("SharedFlowCreation")
     val startedKeyguardState: SharedFlow<KeyguardState> =
         startedKeyguardTransitionStep
             .map { step -> step.to }
             .shareIn(scope, SharingStarted.Eagerly, replay = 1)
 
+    val currentTransitionInfo: StateFlow<TransitionInfo> = repository.currentTransitionInfoInternal
+
+    /** The from state of the last [TransitionState.STARTED] transition. */
+    // TODO: is it performant to have several SharedFlows side by side instead of one?
+    @SuppressLint("SharedFlowCreation")
+    val startedKeyguardFromState: SharedFlow<KeyguardState> =
+        startedKeyguardTransitionStep
+            .map { step -> step.from }
+            .shareIn(scope, SharingStarted.Eagerly, replay = 1)
+
     /** Which keyguard state to use when the device goes to sleep. */
     val asleepKeyguardState: StateFlow<KeyguardState> =
         keyguardRepository.isAodAvailable
@@ -243,6 +314,7 @@
      * sufficient. However, if you're having issues with state *during* transitions started after
      * one or more canceled transitions, you probably need to use [currentKeyguardState].
      */
+    @SuppressLint("SharedFlowCreation")
     val finishedKeyguardState: SharedFlow<KeyguardState> =
         finishedKeyguardTransitionStep
             .map { step -> step.to }
@@ -344,30 +416,37 @@
     val isInTransitionToAnyState = isInTransitionWhere({ true }, { true })
 
     fun transitionStepsFromState(fromState: KeyguardState): Flow<TransitionStep> {
-        return getOrCreateFlow(Edge(from = fromState, to = null))
+        return transition(Edge.create(from = fromState, to = null))
     }
 
     fun transitionStepsToState(toState: KeyguardState): Flow<TransitionStep> {
-        return getOrCreateFlow(Edge(from = null, to = toState))
+        return transition(Edge.create(from = null, to = toState))
     }
 
     /**
      * Called to start a transition that will ultimately dismiss the keyguard from the current
      * state.
+     *
+     * This is called exclusively by sources that can authoritatively say we should be unlocked,
+     * including KeyguardSecurityContainerController and WindowManager.
      */
-    fun startDismissKeyguardTransition() {
-        when (val startedState = startedKeyguardState.replayCache.last()) {
+    fun startDismissKeyguardTransition(reason: String = "") {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
+        Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
+        when (val startedState = currentTransitionInfoInternal.value.to) {
             LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
             PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
             ALTERNATE_BOUNCER ->
                 fromAlternateBouncerTransitionInteractor.get().dismissAlternateBouncer()
             AOD -> fromAodTransitionInteractor.get().dismissAod()
             DOZING -> fromDozingTransitionInteractor.get().dismissFromDozing()
-            else ->
-                Log.e(
-                    "KeyguardTransitionInteractor",
-                    "We don't know how to dismiss keyguard from state $startedState."
+            KeyguardState.GONE ->
+                Log.i(
+                    TAG,
+                    "Already transitioning to GONE; ignoring startDismissKeyguardTransition."
                 )
+            else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
         }
     }
 
@@ -375,7 +454,7 @@
     fun isInTransitionToState(
         state: KeyguardState,
     ): Flow<Boolean> {
-        return getOrCreateFlow(Edge(from = null, to = state))
+        return transition(Edge.create(from = null, to = state))
             .mapLatest { it.transitionState.isTransitioning() }
             .onStart { emit(false) }
             .distinctUntilChanged()
@@ -384,12 +463,16 @@
     /**
      * Whether we're in a transition to and from the given [KeyguardState]s, but haven't yet
      * completed it.
+     *
+     * Provide [edgeWithoutSceneContainer] when the edge is different from what it is without it. If
+     * the edges are equal before and after the flag it is sufficient to provide just [edge].
      */
-    fun isInTransition(
-        from: KeyguardState,
-        to: KeyguardState,
-    ): Flow<Boolean> {
-        return getOrCreateFlow(Edge(from = from, to = to))
+    fun isInTransition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<Boolean> {
+        return if (SceneContainerFlag.isEnabled) {
+                transition(edge)
+            } else {
+                transition(edgeWithoutSceneContainer ?: edge)
+            }
             .mapLatest { it.transitionState.isTransitioning() }
             .onStart { emit(false) }
             .distinctUntilChanged()
@@ -401,7 +484,7 @@
     fun isInTransitionFromState(
         state: KeyguardState,
     ): Flow<Boolean> {
-        return getOrCreateFlow(Edge(from = state, to = null))
+        return transition(Edge.create(from = state, to = null))
             .mapLatest { it.transitionState.isTransitioning() }
             .onStart { emit(false) }
             .distinctUntilChanged()
@@ -452,7 +535,7 @@
      * If you only care about a single state for both from and to, instead use the optimized
      * [isInTransition].
      */
-    fun isInTransitionWhere(
+    private fun isInTransitionWhere(
         fromToStatePredicate: (KeyguardState, KeyguardState) -> Boolean
     ): Flow<Boolean> {
         return repository.transitions
@@ -489,7 +572,23 @@
         return startedKeyguardState.replayCache.last()
     }
 
+    fun getStartedFromState(): KeyguardState {
+        return startedKeyguardFromState.replayCache.last()
+    }
+
     fun getFinishedState(): KeyguardState {
         return finishedKeyguardState.replayCache.last()
     }
+
+    suspend fun startTransition(info: TransitionInfo) = repository.startTransition(info)
+
+    fun updateTransition(
+        transitionId: UUID,
+        @FloatRange(from = 0.0, to = 1.0) value: Float,
+        state: TransitionState
+    ) = repository.updateTransition(transitionId, value, state)
+
+    companion object {
+        private val TAG = KeyguardTransitionInteractor::class.simpleName
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 2d944c6..9443570 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -103,6 +103,7 @@
             KeyguardState.LOCKSCREEN -> true
             KeyguardState.GONE -> true
             KeyguardState.OCCLUDED -> true
+            KeyguardState.UNDEFINED -> true
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index d95c38e..3c66186 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.systemui.CoreStartable
+import com.android.systemui.keyguard.domain.interactor.scenetransition.LockscreenSceneTransitionInteractor
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.ClassKey
@@ -31,6 +32,13 @@
     abstract fun bind(impl: KeyguardTransitionCoreStartable): CoreStartable
 
     @Binds
+    @IntoMap
+    @ClassKey(LockscreenSceneTransitionInteractor::class)
+    abstract fun bindLockscreenSceneTransitionInteractor(
+        impl: LockscreenSceneTransitionInteractor
+    ): CoreStartable
+
+    @Binds
     @IntoSet
     abstract fun fromPrimaryBouncer(
         impl: FromPrimaryBouncerTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index bb2eeb7..1e2db7c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -16,11 +16,17 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
+import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,6 +48,7 @@
     fromBouncerInteractor: FromPrimaryBouncerTransitionInteractor,
     fromAlternateBouncerInteractor: FromAlternateBouncerTransitionInteractor,
     notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor,
+    sceneInteractor: SceneInteractor,
 ) {
     private val defaultSurfaceBehindVisibility =
         transitionInteractor.finishedKeyguardState.map(::isSurfaceVisible)
@@ -103,21 +110,42 @@
      * animation. This is used to keep the RemoteAnimationTarget alive until we're done using it.
      */
     val usingKeyguardGoingAwayAnimation: Flow<Boolean> =
-        combine(
-                transitionInteractor.isInTransitionToState(KeyguardState.GONE),
-                transitionInteractor.finishedKeyguardState,
-                surfaceBehindInteractor.isAnimatingSurface,
-                notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
-            ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning ->
-                // Using the animation if we're animating it directly, or if the
-                // ActivityLaunchAnimator is in the process of animating it.
-                val animationsRunning = isAnimatingSurface || notifLaunchRunning
-                // We may still be animating the surface after the keyguard is fully GONE, since
-                // some animations (like the translation spring) are not tied directly to the
-                // transition step amount.
-                isInTransitionToGone || (finishedState == KeyguardState.GONE && animationsRunning)
-            }
-            .distinctUntilChanged()
+        if (SceneContainerFlag.isEnabled) {
+            combine(
+                    sceneInteractor.transitionState,
+                    surfaceBehindInteractor.isAnimatingSurface,
+                    notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
+                ) { transition, isAnimatingSurface, isLaunchAnimationRunning ->
+                    // Using the animation if we're animating it directly, or if the
+                    // ActivityLaunchAnimator is in the process of animating it.
+                    val isAnyAnimationRunning = isAnimatingSurface || isLaunchAnimationRunning
+                    // We may still be animating the surface after the keyguard is fully GONE, since
+                    // some animations (like the translation spring) are not tied directly to the
+                    // transition step amount.
+                    transition.isTransitioning(to = Scenes.Gone) ||
+                        (isAnyAnimationRunning &&
+                            (transition.isIdle(Scenes.Gone) ||
+                                transition.isTransitioning(from = Scenes.Gone)))
+                }
+                .distinctUntilChanged()
+        } else {
+            combine(
+                    transitionInteractor.isInTransitionToState(KeyguardState.GONE),
+                    transitionInteractor.finishedKeyguardState,
+                    surfaceBehindInteractor.isAnimatingSurface,
+                    notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
+                ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning ->
+                    // Using the animation if we're animating it directly, or if the
+                    // ActivityLaunchAnimator is in the process of animating it.
+                    val animationsRunning = isAnimatingSurface || notifLaunchRunning
+                    // We may still be animating the surface after the keyguard is fully GONE, since
+                    // some animations (like the translation spring) are not tied directly to the
+                    // transition step amount.
+                    isInTransitionToGone ||
+                        (finishedState == KeyguardState.GONE && animationsRunning)
+                }
+                .distinctUntilChanged()
+        }
 
     /**
      * Whether the lockscreen is visible, from the Window Manager (WM) perspective.
@@ -127,28 +155,44 @@
      * want to know if the AOD/clock/notifs/etc. are visible.
      */
     val lockscreenVisibility: Flow<Boolean> =
-        transitionInteractor.currentKeyguardState
-            .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
-            .map { (currentState, startedWithPrev) ->
-                val startedFromStep = startedWithPrev?.previousValue
-                val startedStep = startedWithPrev?.newValue
-                val returningToGoneAfterCancellation =
-                    startedStep?.to == KeyguardState.GONE &&
-                        startedFromStep?.transitionState == TransitionState.CANCELED &&
-                        startedFromStep.from == KeyguardState.GONE
+        if (SceneContainerFlag.isEnabled) {
+            sceneInteractor.transitionState
+                .pairwise(ObservableTransitionState.Idle(Scenes.Lockscreen))
+                .map { (prevTransitionState, transitionState) ->
+                    val isReturningToGoneAfterCancellation =
+                        prevTransitionState.isTransitioning(from = Scenes.Gone) &&
+                            transitionState.isTransitioning(to = Scenes.Gone)
+                    val isNotOnGone =
+                        !transitionState.isTransitioning(from = Scenes.Gone) &&
+                            !transitionState.isIdle(Scenes.Gone)
 
-                if (!returningToGoneAfterCancellation) {
-                    // By default, apply the lockscreen visibility of the current state.
-                    KeyguardState.lockscreenVisibleInState(currentState)
-                } else {
-                    // If we're transitioning to GONE after a prior canceled transition from GONE,
-                    // then this is the camera launch transition from an asleep state back to GONE.
-                    // We don't want to show the lockscreen since we're aborting the lock and going
-                    // back to GONE.
-                    KeyguardState.lockscreenVisibleInState(KeyguardState.GONE)
+                    isNotOnGone && !isReturningToGoneAfterCancellation
                 }
-            }
-            .distinctUntilChanged()
+                .distinctUntilChanged()
+        } else {
+            transitionInteractor.currentKeyguardState
+                .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
+                .map { (currentState, startedWithPrev) ->
+                    val startedFromStep = startedWithPrev?.previousValue
+                    val startedStep = startedWithPrev?.newValue
+                    val returningToGoneAfterCancellation =
+                        startedStep?.to == KeyguardState.GONE &&
+                            startedFromStep?.transitionState == TransitionState.CANCELED &&
+                            startedFromStep.from == KeyguardState.GONE
+
+                    if (!returningToGoneAfterCancellation) {
+                        // By default, apply the lockscreen visibility of the current state.
+                        KeyguardState.lockscreenVisibleInState(currentState)
+                    } else {
+                        // If we're transitioning to GONE after a prior canceled transition from
+                        // GONE, then this is the camera launch transition from an asleep state back
+                        // to GONE. We don't want to show the lockscreen since we're aborting the
+                        // lock and going back to GONE.
+                        KeyguardState.lockscreenVisibleInState(KeyguardState.GONE)
+                    }
+                }
+                .distinctUntilChanged()
+        }
 
     /**
      * Whether always-on-display (AOD) is visible when the lockscreen is visible, from window
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
new file mode 100644
index 0000000..3baeb76
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor.scenetransition
+
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.LockscreenSceneTransitionRepository
+import com.android.systemui.keyguard.data.repository.LockscreenSceneTransitionRepository.Companion.DEFAULT_STATE
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.pairwise
+import java.util.UUID
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+/**
+ * This class listens to scene framework scene transitions and manages keyguard transition framework
+ * (KTF) states accordingly.
+ *
+ * There are a few rules:
+ * - When scene framework is on a scene outside of Lockscreen, then KTF is in state UNDEFINED
+ * - When scene framework is on Lockscreen, KTF is allowed to change its scenes freely
+ * - When scene framework is transitioning away from Lockscreen, then KTF transitions to UNDEFINED
+ *   and shares its progress.
+ * - When scene framework is transitioning to Lockscreen, then KTF starts a transition to LOCKSCREEN
+ *   but it is allowed to interrupt this transition and transition to other internal KTF states
+ *
+ * There are a few notable differences between SceneTransitionLayout (STL) and KTF that require
+ * special treatment when synchronizing both state machines.
+ * - STL does not emit cancelations as KTF does
+ * - Both STL and KTF require state continuity, though the rules from where starting the next
+ *   transition is allowed is different on each side:
+ *     - STL has a concept of "currentScene" which can be chosen to be either A or B in a A -> B
+ *       transition. The currentScene determines which transition can be started next. In KTF the
+ *       currentScene is always the `to` state. Which means transitions can only be started from B.
+ *       This also holds true when A -> B was canceled: the next transition needs to start from B.
+ *     - KTF can not settle back in its from scene, instead it needs to cancel and start a reversed
+ *       transition.
+ */
+@SysUISingleton
+class LockscreenSceneTransitionInteractor
+@Inject
+constructor(
+    val transitionInteractor: KeyguardTransitionInteractor,
+    @Application private val applicationScope: CoroutineScope,
+    private val sceneInteractor: SceneInteractor,
+    private val repository: LockscreenSceneTransitionRepository,
+) : CoreStartable, SceneInteractor.OnSceneAboutToChangeListener {
+
+    private var currentTransitionId: UUID? = null
+    private var progressJob: Job? = null
+
+    override fun start() {
+        sceneInteractor.registerSceneStateProcessor(this)
+        listenForSceneTransitionProgress()
+    }
+
+    override fun onSceneAboutToChange(toScene: SceneKey, sceneState: Any?) {
+        if (toScene != Scenes.Lockscreen || sceneState == null) return
+        if (sceneState !is KeyguardState) {
+            throw IllegalArgumentException("Lockscreen sceneState needs to be a KeyguardState.")
+        }
+        repository.nextLockscreenTargetState.value = sceneState
+    }
+
+    private fun listenForSceneTransitionProgress() {
+        applicationScope.launch {
+            sceneInteractor.transitionState
+                .pairwise(ObservableTransitionState.Idle(Scenes.Lockscreen))
+                .collect { (prevTransition, transition) ->
+                    when (transition) {
+                        is ObservableTransitionState.Idle -> handleIdle(prevTransition, transition)
+                        is ObservableTransitionState.Transition -> handleTransition(transition)
+                    }
+                }
+        }
+    }
+
+    private suspend fun handleIdle(
+        prevTransition: ObservableTransitionState,
+        idle: ObservableTransitionState.Idle
+    ) {
+        if (currentTransitionId == null) return
+        if (prevTransition !is ObservableTransitionState.Transition) return
+
+        if (idle.currentScene == prevTransition.toScene) {
+            finishCurrentTransition()
+        } else {
+            val targetState =
+                if (idle.currentScene == Scenes.Lockscreen) {
+                    transitionInteractor.getStartedFromState()
+                } else {
+                    UNDEFINED
+                }
+            finishReversedTransitionTo(targetState)
+        }
+    }
+
+    private fun finishCurrentTransition() {
+        transitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
+        resetTransitionData()
+    }
+
+    private suspend fun finishReversedTransitionTo(state: KeyguardState) {
+        val newTransition =
+            TransitionInfo(
+                ownerName = this::class.java.simpleName,
+                from = transitionInteractor.currentTransitionInfo.value.to,
+                to = state,
+                animator = null,
+                modeOnCanceled = TransitionModeOnCanceled.REVERSE
+            )
+        currentTransitionId = transitionInteractor.startTransition(newTransition)
+        transitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
+        resetTransitionData()
+    }
+
+    private fun resetTransitionData() {
+        progressJob?.cancel()
+        progressJob = null
+        currentTransitionId = null
+    }
+
+    private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
+        if (transition.fromScene == Scenes.Lockscreen) {
+            if (currentTransitionId != null) {
+                val currentToState = transitionInteractor.currentTransitionInfo.value.to
+                if (currentToState == UNDEFINED) {
+                    transitionKtfTo(transitionInteractor.getStartedFromState())
+                }
+            }
+            startTransitionFromLockscreen()
+            collectProgress(transition)
+        } else if (transition.toScene == Scenes.Lockscreen) {
+            if (currentTransitionId != null) {
+                transitionKtfTo(UNDEFINED)
+            }
+            startTransitionToLockscreen()
+            collectProgress(transition)
+        } else {
+            transitionKtfTo(UNDEFINED)
+        }
+    }
+
+    private suspend fun transitionKtfTo(state: KeyguardState) {
+        // TODO(b/330311871): This is based on a sharedFlow and thus might not be up-to-date and
+        //  cause a race condition. (There is no known scenario that is currently affected.)
+        val currentTransition = transitionInteractor.transitionState.value
+        if (currentTransition.isFinishedIn(state)) {
+            // This is already the state we want to be in
+            resetTransitionData()
+        } else if (currentTransition.isTransitioning(to = state)) {
+            finishCurrentTransition()
+        } else {
+            finishReversedTransitionTo(state)
+        }
+    }
+
+    private fun collectProgress(transition: ObservableTransitionState.Transition) {
+        progressJob?.cancel()
+        progressJob = applicationScope.launch { transition.progress.collect { updateProgress(it) } }
+    }
+
+    private suspend fun startTransitionToLockscreen() {
+        val newTransition =
+            TransitionInfo(
+                ownerName = this::class.java.simpleName,
+                from = UNDEFINED,
+                to = repository.nextLockscreenTargetState.value,
+                animator = null,
+                modeOnCanceled = TransitionModeOnCanceled.RESET
+            )
+        repository.nextLockscreenTargetState.value = DEFAULT_STATE
+        startTransition(newTransition)
+    }
+
+    private suspend fun startTransitionFromLockscreen() {
+        val currentState = transitionInteractor.currentTransitionInfo.value.to
+        val newTransition =
+            TransitionInfo(
+                ownerName = this::class.java.simpleName,
+                from = currentState,
+                to = UNDEFINED,
+                animator = null,
+                modeOnCanceled = TransitionModeOnCanceled.RESET
+            )
+        startTransition(newTransition)
+    }
+
+    private suspend fun startTransition(transitionInfo: TransitionInfo) {
+        if (currentTransitionId != null) {
+            resetTransitionData()
+        }
+        currentTransitionId = transitionInteractor.startTransition(transitionInfo)
+    }
+
+    private fun updateProgress(progress: Float) {
+        if (currentTransitionId == null) return
+        transitionInteractor.updateTransition(
+            currentTransitionId!!,
+            progress.coerceIn(0f, 1f),
+            RUNNING
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
index a0f9be6..4f516f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
@@ -15,8 +15,98 @@
  */
 package com.android.systemui.keyguard.shared.model
 
-/** FROM -> TO keyguard transition. null values are allowed to signify FROM -> *, or * -> TO */
-data class Edge(
-    val from: KeyguardState?,
-    val to: KeyguardState?,
-)
+import android.util.Log
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+
+/**
+ * Represents an edge either between two Keyguard Transition Framework states (KTF) or a KTF state
+ * and a scene container scene. Passing [null] in either [from] or [to] indicates a wildcard.
+ *
+ * Wildcards are not allowed for transitions involving a scene. Use [sceneInteractor] directly
+ * instead. Reason: [TransitionStep]s are not emitted for every edge leading into/out of a scene.
+ * For example: Lockscreen -> Gone would be emitted as LOCKSCREEN -> UNDEFINED but Bouncer -> Gone
+ * would not emit anything.
+ */
+sealed class Edge {
+
+    fun verifyValidKeyguardStates() {
+        when (this) {
+            is StateToState -> verifyValidKeyguardStates(from, to)
+            is SceneToState -> verifyValidKeyguardStates(null, to)
+            is StateToScene -> verifyValidKeyguardStates(from, null)
+        }
+    }
+
+    private fun verifyValidKeyguardStates(from: KeyguardState?, to: KeyguardState?) {
+        val mappedFrom = from?.mapToSceneContainerState()
+        val mappedTo = to?.mapToSceneContainerState()
+
+        val fromChanged = from != mappedFrom
+        val toChanged = to != mappedTo
+
+        if (SceneContainerFlag.isEnabled) {
+            if (fromChanged && toChanged) {
+                // TODO:(b/330311871) As we come close to having all current edges converted these
+                //  error messages can be converted to throw such that future developers fail early
+                //  when they introduce invalid edges.
+                Log.e(
+                    TAG,
+                    """
+                    The edge ${from?.name} => ${to?.name} was automatically converted to
+                    ${mappedFrom?.name} => ${mappedTo?.name} but does not exist anymore in KTF.
+                    Please remove or port this edge to scene container."""
+                        .trimIndent(),
+                )
+            } else if ((fromChanged && to == null) || (toChanged && from == null)) {
+                Log.e(
+                    TAG,
+                    """
+                    The edge ${from?.name} => ${to?.name} was automatically converted to
+                    ${mappedFrom?.name} => ${mappedTo?.name}. Wildcards are not allowed together
+                    with UNDEFINED because it will only be tracking edges leading in and out of
+                    the Lockscreen scene but miss others. Please remove or port this edge."""
+                        .trimIndent(),
+                    Exception()
+                )
+            } else if (fromChanged || toChanged) {
+                Log.w(
+                    TAG,
+                    """
+                    The edge ${from?.name} => ${to?.name} was automatically converted to
+                    ${mappedFrom?.name} => ${mappedTo?.name} it probably exists but needs explicit
+                    conversion. Please remove or port this edge to scene container."""
+                        .trimIndent(),
+                )
+            }
+        } else {
+            if (from == UNDEFINED || to == UNDEFINED) {
+                Log.e(
+                    TAG,
+                    "UNDEFINED should not be used when scene container is disabled",
+                )
+            }
+        }
+    }
+
+    data class StateToState(val from: KeyguardState?, val to: KeyguardState?) : Edge() {
+        init {
+            check(!(from == null && to == null)) { "to and from can't both be null" }
+        }
+    }
+
+    data class StateToScene(val from: KeyguardState, val to: SceneKey) : Edge()
+
+    data class SceneToState(val from: SceneKey, val to: KeyguardState) : Edge()
+
+    companion object {
+        private const val TAG = "Edge"
+
+        fun create(from: KeyguardState? = null, to: KeyguardState? = null) = StateToState(from, to)
+
+        fun create(from: KeyguardState, to: SceneKey) = StateToScene(from, to)
+
+        fun create(from: SceneKey, to: KeyguardState) = SceneToState(from, to)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index 7d05539..6a2bb5f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -15,9 +15,12 @@
  */
 package com.android.systemui.keyguard.shared.model
 
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
+
 /** List of all possible states to transition to/from */
 enum class KeyguardState {
-    /*
+    /**
      * The display is completely off, as well as any sensors that would trigger the device to wake
      * up.
      */
@@ -29,13 +32,13 @@
      * notifications is enabled, allowing the device to quickly wake up.
      */
     DOZING,
-    /*
+    /**
      * A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
      * DOZING is an example of special version of this state. Dreams may be implemented by third
      * parties to present their own UI over keyguard, like a screensaver.
      */
     DREAMING,
-    /*
+    /**
      * A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
      * It is a special version of DREAMING state but not DOZING. The active dream will be windowless
      * and hosted in the lockscreen.
@@ -47,17 +50,17 @@
      * low-power mode without a UI, then it is DOZING.
      */
     AOD,
-    /*
+    /**
      * The security screen prompt containing UI to prompt the user to use a biometric credential
      * (ie: fingerprint). When supported, this may show before showing the primary bouncer.
      */
     ALTERNATE_BOUNCER,
-    /*
+    /**
      * The security screen prompt UI, containing PIN, Password, Pattern for the user to verify their
      * credentials.
      */
     PRIMARY_BOUNCER,
-    /*
+    /**
      * Device is actively displaying keyguard UI and is not in low-power mode. Device may be
      * unlocked if SWIPE security method is used, or if face lockscreen bypass is false.
      */
@@ -68,17 +71,56 @@
      * or dream, as well as swipe down for the notifications and up for the bouncer.
      */
     GLANCEABLE_HUB,
-    /*
-     * Keyguard is no longer visible. In most cases the user has just authenticated and keyguard
-     * is being removed, but there are other cases where the user is swiping away keyguard, such as
+    /**
+     * Keyguard is no longer visible. In most cases the user has just authenticated and keyguard is
+     * being removed, but there are other cases where the user is swiping away keyguard, such as
      * with SWIPE security method or face unlock without bypass.
      */
     GONE,
-    /*
-     * An activity is displaying over the keyguard.
+    /**
+     * Only used in scene framework. This means we are currently on any scene framework scene that
+     * is not Lockscreen. Transitions to and from UNDEFINED are always bound to the
+     * [SceneTransitionLayout] scene transition that either transitions to or from the Lockscreen
+     * scene. These transitions are automatically handled by [LockscreenSceneTransitionInteractor].
      */
+    UNDEFINED,
+    /** An activity is displaying over the keyguard. */
     OCCLUDED;
 
+    fun mapToSceneContainerState(): KeyguardState {
+        return when (this) {
+            OFF,
+            DOZING,
+            DREAMING,
+            DREAMING_LOCKSCREEN_HOSTED,
+            AOD,
+            ALTERNATE_BOUNCER,
+            OCCLUDED,
+            LOCKSCREEN -> this
+            GLANCEABLE_HUB,
+            PRIMARY_BOUNCER,
+            GONE,
+            UNDEFINED -> UNDEFINED
+        }
+    }
+
+    fun mapToSceneContainerScene(): SceneKey? {
+        return when (this) {
+            OFF,
+            DOZING,
+            DREAMING,
+            DREAMING_LOCKSCREEN_HOSTED,
+            AOD,
+            ALTERNATE_BOUNCER,
+            OCCLUDED,
+            LOCKSCREEN -> Scenes.Lockscreen
+            GLANCEABLE_HUB -> Scenes.Communal
+            PRIMARY_BOUNCER -> Scenes.Bouncer
+            GONE -> Scenes.Gone
+            UNDEFINED -> null
+        }
+    }
+
     companion object {
 
         /** Whether the lockscreen is visible when we're FINISHED in the given state. */
@@ -109,6 +151,7 @@
                 LOCKSCREEN -> true
                 GONE -> true
                 OCCLUDED -> true
+                UNDEFINED -> true
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
index 0fa6f4f..2b4c4af 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
@@ -30,4 +30,12 @@
         value: Float,
         transitionState: TransitionState,
     ) : this(info.from, info.to, value, transitionState, info.ownerName)
+
+    fun isTransitioning(from: KeyguardState? = null, to: KeyguardState? = null): Boolean {
+        return (from == null || this.from == from) && (to == null || this.to == to)
+    }
+
+    fun isFinishedIn(state: KeyguardState): Boolean {
+        return to == state && transitionState == TransitionState.FINISHED
+    }
 }
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 735b109..23aa21c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
 import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import javax.inject.Inject
 import kotlin.math.max
 import kotlin.math.min
@@ -52,20 +53,20 @@
     /** Invoke once per transition between FROM->TO states to get access to a shared flow. */
     fun setup(
         duration: Duration,
-        from: KeyguardState?,
-        to: KeyguardState?,
+        edge: Edge,
     ): FlowBuilder {
-        if (from == null && to == null) {
-            throw IllegalArgumentException("from and to are both null")
-        }
-
-        return FlowBuilder(duration, Edge(from, to))
+        return FlowBuilder(duration, edge)
     }
 
     inner class FlowBuilder(
         private val transitionDuration: Duration,
         private val edge: Edge,
     ) {
+        fun setupWithoutSceneContainer(edge: Edge.StateToState): FlowBuilder {
+            if (SceneContainerFlag.isEnabled) return this
+            return setup(this.transitionDuration, edge)
+        }
+
         /**
          * Transitions will occur over a [transitionDuration] with [TransitionStep]s being emitted
          * in the range of [0, 1]. View animations should begin and end within a subset of this
@@ -117,7 +118,7 @@
             if (!duration.isPositive()) {
                 throw IllegalArgumentException("duration must be a positive number: $duration")
             }
-            if ((startTime + duration).compareTo(transitionDuration) > 0) {
+            if ((startTime + duration) > transitionDuration) {
                 throw IllegalArgumentException(
                     "startTime($startTime) + duration($duration) must be" +
                         " <= transitionDuration($transitionDuration)"
@@ -153,7 +154,7 @@
             }
 
             return transitionInteractor
-                .getOrCreateFlow(edge)
+                .transition(edge)
                 .map { step ->
                     StateToValue(
                             from = step.from,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index e2b66c5..1c7b4d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -22,6 +22,8 @@
 import android.util.StateSet
 import android.view.HapticFeedbackConstants
 import android.view.View
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
 import androidx.core.view.isInvisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
@@ -61,6 +63,7 @@
         bgViewModel: DeviceEntryBackgroundViewModel,
         falsingManager: FalsingManager,
         vibratorHelper: VibratorHelper,
+        overrideColor: Color? = null,
     ) {
         DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
         val longPressHandlingView = view.longPressHandlingView
@@ -168,7 +171,8 @@
                                     viewModel.type.contentDescriptionResId
                                 )
                         }
-                        fgIconView.imageTintList = ColorStateList.valueOf(viewModel.tint)
+                        fgIconView.imageTintList =
+                            ColorStateList.valueOf(overrideColor?.toArgb() ?: viewModel.tint)
                         fgIconView.setPadding(
                             viewModel.padding,
                             viewModel.padding,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index ed5d53c..c846cbe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -33,14 +33,18 @@
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
 import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.clocks.AodClockBurnInModel
 import com.android.systemui.plugins.clocks.ClockController
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
 object KeyguardClockViewBinder {
     private val TAG = KeyguardClockViewBinder::class.simpleName!!
     // When changing to new clock, we need to remove old clock views from burnInLayer
     private var lastClock: ClockController? = null
+
     @JvmStatic
     fun bind(
         clockSection: ClockSection,
@@ -48,6 +52,7 @@
         viewModel: KeyguardClockViewModel,
         keyguardClockInteractor: KeyguardClockInteractor,
         blueprintInteractor: KeyguardBlueprintInteractor,
+        rootViewModel: KeyguardRootViewModel,
     ) {
         keyguardRootView.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
@@ -105,6 +110,28 @@
                         }
                     }
                 }
+
+                launch {
+                    if (!MigrateClocksToBlueprint.isEnabled) return@launch
+                    combine(
+                            rootViewModel.translationX,
+                            rootViewModel.translationY,
+                            rootViewModel.scale,
+                            ::Triple
+                        )
+                        .collect { (translationX, translationY, scale) ->
+                            viewModel.currentClock.value
+                                ?.largeClock
+                                ?.layout
+                                ?.applyAodBurnIn(
+                                    AodClockBurnInModel(
+                                        translationX = translationX.value!!,
+                                        translationY = translationY,
+                                        scale = scale.scale
+                                    )
+                                )
+                        }
+                }
             }
         }
     }
@@ -117,17 +144,16 @@
     ) {
         val burnInLayer = viewModel.burnInLayer
         val clockController = viewModel.currentClock.value
+        // Large clocks won't be added to or removed from burn in layer
+        // Weather large clock has customized burn in preventing mechanism
+        // Non-weather large clock will only scale and translate vertically
         clockController?.let { clock ->
             when (clockSize) {
                 ClockSize.LARGE -> {
                     clock.smallClock.layout.views.forEach { burnInLayer?.removeView(it) }
-                    if (clock.config.useAlternateSmartspaceAODTransition) {
-                        clock.largeClock.layout.views.forEach { burnInLayer?.addView(it) }
-                    }
                 }
                 ClockSize.SMALL -> {
                     clock.smallClock.layout.views.forEach { burnInLayer?.addView(it) }
-                    clock.largeClock.layout.views.forEach { burnInLayer?.removeView(it) }
                 }
             }
         }
@@ -148,13 +174,6 @@
                 burnInLayer?.removeView(it)
                 rootView.removeView(it)
             }
-
-            // add large clock to burn in layer only when it will have same transition with other
-            // components in AOD otherwise, it will have a separate scale transition while other
-            // components only have translate transition
-            if (clock.config.useAlternateSmartspaceAODTransition) {
-                clock.largeClock.layout.views.forEach { burnInLayer?.removeView(it) }
-            }
             clock.largeClock.layout.views.forEach { rootView.removeView(it) }
         }
         lastClock = currentClock
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index bda6438..39db22d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -236,7 +236,8 @@
                                                 indicationArea,
                                                 startButton,
                                                 endButton,
-                                                lockIcon -> {
+                                                lockIcon,
+                                                deviceEntryIcon -> {
                                                     // Do not move these views
                                                 }
                                                 else -> childView.translationX = px
@@ -256,23 +257,6 @@
                                         it.scaleX = scaleViewModel.scale
                                         it.scaleY = scaleViewModel.scale
                                     }
-                                    // Make sure to reset these views, or they will be invisible
-                                    if (childViews[burnInLayerId]?.scaleX != 1f) {
-                                        childViews[burnInLayerId]?.scaleX = 1f
-                                        childViews[burnInLayerId]?.scaleY = 1f
-                                        childViews[aodNotificationIconContainerId]?.scaleX = 1f
-                                        childViews[aodNotificationIconContainerId]?.scaleY = 1f
-                                        view.requestLayout()
-                                    }
-                                } else {
-                                    // For weather clock, large clock should have only scale
-                                    // transition with other parts in burnInLayer
-                                    childViews[burnInLayerId]?.scaleX = scaleViewModel.scale
-                                    childViews[burnInLayerId]?.scaleY = scaleViewModel.scale
-                                    childViews[aodNotificationIconContainerId]?.scaleX =
-                                        scaleViewModel.scale
-                                    childViews[aodNotificationIconContainerId]?.scaleY =
-                                        scaleViewModel.scale
                                 }
                             }
                         }
@@ -596,6 +580,7 @@
     private val startButton = R.id.start_button
     private val endButton = R.id.end_button
     private val lockIcon = R.id.lock_icon_view
+    private val deviceEntryIcon = R.id.device_entry_icon_view
     private val nsslPlaceholderId = R.id.nssl_placeholder
 
     private const val ID = "occluding_app_device_entry_unlock_msg"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index 0f63f65..1f4bc61 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -29,7 +29,10 @@
 import com.android.systemui.keyguard.ui.viewmodel.DozingToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DozingToPrimaryBouncerTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GoneToLockscreenTransitionViewModel
@@ -40,6 +43,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludedToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToGlanceableHubTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OffToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToAodTransitionViewModel
@@ -218,4 +222,28 @@
     abstract fun primaryBouncerToLockscreen(
         impl: PrimaryBouncerToLockscreenTransitionViewModel
     ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun dreamingToGlanceableHub(
+        impl: DreamingToGlanceableHubTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun glanceableHubToDreaming(
+        impl: GlanceableHubToDreamingTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun glanceableHubToOccluded(
+        impl: GlanceableHubToOccludedTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun occludedToGlanceableHub(
+        impl: OccludedToGlanceableHubTransitionViewModel
+    ): DeviceEntryIconTransition
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index ef29270..b367715 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.binder.KeyguardClockViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockFaceLayout
@@ -64,6 +65,7 @@
     private val context: Context,
     val smartspaceViewModel: KeyguardSmartspaceViewModel,
     val blueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
+    private val rootViewModel: KeyguardRootViewModel,
 ) : KeyguardSection() {
     override fun addViews(constraintLayout: ConstraintLayout) {}
     override fun bindData(constraintLayout: ConstraintLayout) {
@@ -75,7 +77,8 @@
             constraintLayout,
             keyguardClockViewModel,
             clockInteractor,
-            blueprintInteractor.get()
+            blueprintInteractor.get(),
+            rootViewModel,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 218967c..7c745bc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -57,9 +57,9 @@
         addTransition(SmartspaceMoveTransition(config, clockViewModel))
     }
 
-    open class VisibilityBoundsTransition() : Transition() {
-        var captureSmartspace: Boolean = false
-
+    abstract class VisibilityBoundsTransition() : Transition() {
+        abstract val captureSmartspace: Boolean
+        protected val TAG = this::class.simpleName!!
         override fun captureEndValues(transition: TransitionValues) = captureValues(transition)
         override fun captureStartValues(transition: TransitionValues) = captureValues(transition)
         override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES
@@ -76,7 +76,7 @@
                 parent.findViewById<View>(sharedR.id.bc_smartspace_view)
                     ?: parent.findViewById<View>(R.id.keyguard_slice_view)
             if (targetSSView == null) {
-                Log.e(TAG, "Failed to find smartspace equivalent target for animation")
+                Log.e(TAG, "Failed to find smartspace equivalent target under $parent")
                 return
             }
             transition.values[SMARTSPACE_BOUNDS] = targetSSView.getRect()
@@ -109,14 +109,12 @@
             var fromIsVis = fromVis == View.VISIBLE
             var fromAlpha = startValues.values[PROP_ALPHA] as Float
             val fromBounds = startValues.values[PROP_BOUNDS] as Rect
-            val fromSSBounds =
-                if (captureSmartspace) startValues.values[SMARTSPACE_BOUNDS] as Rect else null
+            val fromSSBounds = startValues.values[SMARTSPACE_BOUNDS] as Rect?
 
             val toView = endValues.view
             val toVis = endValues.values[PROP_VISIBILITY] as Int
             val toBounds = endValues.values[PROP_BOUNDS] as Rect
-            val toSSBounds =
-                if (captureSmartspace) endValues.values[SMARTSPACE_BOUNDS] as Rect else null
+            val toSSBounds = endValues.values[SMARTSPACE_BOUNDS] as Rect?
             val toIsVis = toVis == View.VISIBLE
             val toAlpha = if (toIsVis) 1f else 0f
 
@@ -221,9 +219,6 @@
             private const val SMARTSPACE_BOUNDS = "ClockSizeTransition:SSBounds"
             private val TRANSITION_PROPERTIES =
                 arrayOf(PROP_VISIBILITY, PROP_ALPHA, PROP_BOUNDS, SMARTSPACE_BOUNDS)
-
-            private val DEBUG = false
-            private val TAG = VisibilityBoundsTransition::class.simpleName!!
         }
     }
 
@@ -232,18 +227,24 @@
         val viewModel: KeyguardClockViewModel,
         val smartspaceViewModel: KeyguardSmartspaceViewModel,
     ) : VisibilityBoundsTransition() {
+        override val captureSmartspace = !viewModel.isLargeClockVisible.value
+
         init {
             duration = CLOCK_IN_MILLIS
             startDelay = CLOCK_IN_START_DELAY_MILLIS
             interpolator = CLOCK_IN_INTERPOLATOR
-            captureSmartspace =
-                !viewModel.isLargeClockVisible.value && smartspaceViewModel.isSmartspaceEnabled
 
             if (viewModel.isLargeClockVisible.value) {
                 viewModel.currentClock.value?.let {
+                    if (DEBUG) Log.i(TAG, "Large Clock In: ${it.largeClock.layout.views}")
                     it.largeClock.layout.views.forEach { addTarget(it) }
                 }
+                    ?: run {
+                        Log.e(TAG, "No large clock set, falling back")
+                        addTarget(R.id.lockscreen_clock_view_large)
+                    }
             } else {
+                if (DEBUG) Log.i(TAG, "Small Clock In")
                 addTarget(R.id.lockscreen_clock_view)
             }
         }
@@ -282,7 +283,6 @@
             val CLOCK_IN_INTERPOLATOR = Interpolators.LINEAR_OUT_SLOW_IN
             const val SMALL_CLOCK_IN_MOVE_SCALE =
                 CLOCK_IN_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_DOWN_MILLIS.toFloat()
-            private val TAG = ClockFaceInTransition::class.simpleName!!
         }
     }
 
@@ -291,18 +291,24 @@
         val viewModel: KeyguardClockViewModel,
         val smartspaceViewModel: KeyguardSmartspaceViewModel,
     ) : VisibilityBoundsTransition() {
+        override val captureSmartspace = viewModel.isLargeClockVisible.value
+
         init {
             duration = CLOCK_OUT_MILLIS
             interpolator = CLOCK_OUT_INTERPOLATOR
-            captureSmartspace =
-                viewModel.isLargeClockVisible.value && smartspaceViewModel.isSmartspaceEnabled
 
             if (viewModel.isLargeClockVisible.value) {
+                if (DEBUG) Log.i(TAG, "Small Clock Out")
                 addTarget(R.id.lockscreen_clock_view)
             } else {
                 viewModel.currentClock.value?.let {
+                    if (DEBUG) Log.i(TAG, "Large Clock Out: ${it.largeClock.layout.views}")
                     it.largeClock.layout.views.forEach { addTarget(it) }
                 }
+                    ?: run {
+                        Log.e(TAG, "No large clock set, falling back")
+                        addTarget(R.id.lockscreen_clock_view_large)
+                    }
             }
         }
 
@@ -339,7 +345,6 @@
             val CLOCK_OUT_INTERPOLATOR = Interpolators.LINEAR
             const val SMALL_CLOCK_OUT_MOVE_SCALE =
                 CLOCK_OUT_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_UP_MILLIS.toFloat()
-            private val TAG = ClockFaceOutTransition::class.simpleName!!
         }
     }
 
@@ -348,6 +353,8 @@
         val config: IntraBlueprintTransition.Config,
         viewModel: KeyguardClockViewModel,
     ) : VisibilityBoundsTransition() {
+        override val captureSmartspace = false
+
         init {
             duration =
                 if (viewModel.isLargeClockVisible.value) STATUS_AREA_MOVE_UP_MILLIS
@@ -367,4 +374,8 @@
             const val STATUS_AREA_MOVE_DOWN_MILLIS = 467L
         }
     }
+
+    companion object {
+        val DEBUG = true
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
index 4fd92d7..9da11ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
@@ -19,7 +19,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -42,8 +44,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION,
-            from = KeyguardState.ALTERNATE_BOUNCER,
-            to = KeyguardState.AOD,
+            edge = Edge.create(from = ALTERNATE_BOUNCER, to = AOD),
         )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
index 9649af73..55a48b6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
@@ -19,7 +19,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -42,8 +44,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromAlternateBouncerTransitionInteractor.TO_DOZING_DURATION,
-            from = KeyguardState.ALTERNATE_BOUNCER,
-            to = KeyguardState.DOZING,
+            edge = Edge.create(from = ALTERNATE_BOUNCER, to = DOZING),
         )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
index 8c6be98..bb4fb79 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
@@ -19,11 +19,13 @@
 import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_GONE_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.ScrimAlpha
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -44,11 +46,14 @@
     private val statusBarStateController: SysuiStatusBarStateController,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_GONE_DURATION,
-            from = ALTERNATE_BOUNCER,
-            to = KeyguardState.GONE,
-        )
+        animationFlow
+            .setup(
+                duration = TO_GONE_DURATION,
+                edge = Edge.create(from = ALTERNATE_BOUNCER, to = Scenes.Gone),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = ALTERNATE_BOUNCER, to = GONE),
+            )
 
     fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
         var startAlpha = 1f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
index 27febd3..3f2ef29 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
@@ -18,8 +18,9 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_OCCLUDED_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -40,8 +41,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_OCCLUDED_DURATION,
-            from = ALTERNATE_BOUNCER,
-            to = KeyguardState.OCCLUDED,
+            edge = Edge.create(from = ALTERNATE_BOUNCER, to = OCCLUDED),
         )
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
index 7592881..f0bccac 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -37,11 +40,14 @@
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
-            from = KeyguardState.ALTERNATE_BOUNCER,
-            to = KeyguardState.PRIMARY_BOUNCER,
-        )
+        animationFlow
+            .setup(
+                duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+                edge = Edge.create(from = ALTERNATE_BOUNCER, to = Scenes.Bouncer),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = ALTERNATE_BOUNCER, to = PRIMARY_BOUNCER),
+            )
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
index 5cf100e..4128c52 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
@@ -34,8 +34,8 @@
     alternateBouncerInteractor: AlternateBouncerInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
 ) {
-    private val deviceSupportsAlternateBouncer: Flow<Boolean> =
-        alternateBouncerInteractor.alternateBouncerSupported
+    val canShowAlternateBouncer: Flow<Boolean> = alternateBouncerInteractor.canShowAlternateBouncer
+
     private val isTransitioningToOrFromOrShowingAlternateBouncer: Flow<Boolean> =
         keyguardTransitionInteractor
             .transitionValue(KeyguardState.ALTERNATE_BOUNCER)
@@ -43,8 +43,8 @@
             .distinctUntilChanged()
 
     val alternateBouncerWindowRequired: Flow<Boolean> =
-        deviceSupportsAlternateBouncer.flatMapLatest { deviceSupportsAlternateBouncer ->
-            if (deviceSupportsAlternateBouncer) {
+        canShowAlternateBouncer.flatMapLatest { canShowAlternateBouncer ->
+            if (canShowAlternateBouncer) {
                 isTransitioningToOrFromOrShowingAlternateBouncer
             } else {
                 flowOf(false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 5b83a10..c05a1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.ComposeLockscreen
 import com.android.systemui.keyguard.shared.model.BurnInModel
 import com.android.systemui.keyguard.shared.model.ClockSize
 import com.android.systemui.keyguard.ui.StateToValue
@@ -121,11 +120,13 @@
             ),
         ) { interpolated, burnIn ->
             val useAltAod =
-                keyguardClockViewModel.currentClock.value?.let { clock ->
-                    clock.config.useAlternateSmartspaceAODTransition
-                } == true
+                keyguardClockViewModel.currentClock.value
+                    ?.config
+                    ?.useAlternateSmartspaceAODTransition == true
+            // Only scale large non-weather clocks
+            // elements in large weather clock will translate the same as smartspace
             val useScaleOnly =
-                useAltAod && keyguardClockViewModel.clockSize.value == ClockSize.LARGE
+                (!useAltAod) && keyguardClockViewModel.clockSize.value == ClockSize.LARGE
 
             val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolated).toInt()
             val translationY =
@@ -134,35 +135,12 @@
                 } else {
                     max(params.topInset, params.minViewY + burnInY) - params.minViewY
                 }
-            if (ComposeLockscreen.isEnabled) {
-                BurnInModel(
-                    translationX = MathUtils.lerp(0, burnIn.translationX, interpolated).toInt(),
-                    translationY = translationY,
-                    scale = MathUtils.lerp(burnIn.scale, 1f, 1f - interpolated),
-                    scaleClockOnly = !useScaleOnly,
-                )
-            } else {
-                if (useScaleOnly) {
-                    BurnInModel(
-                        translationX = 0,
-                        translationY = 0,
-                        scale = MathUtils.lerp(burnIn.scale, 1f, 1f - interpolated),
-                    )
-                } else {
-                    // Ensure the desired translation doesn't encroach on the top inset
-                    BurnInModel(
-                        translationX = MathUtils.lerp(0, burnIn.translationX, interpolated).toInt(),
-                        translationY = translationY,
-                        scale =
-                            MathUtils.lerp(
-                                /* start= */ burnIn.scale,
-                                /* stop= */ 1f,
-                                /* amount= */ 1f - interpolated,
-                            ),
-                        scaleClockOnly = true,
-                    )
-                }
-            }
+            BurnInModel(
+                translationX = MathUtils.lerp(0, burnIn.translationX, interpolated).toInt(),
+                translationY = translationY,
+                scale = MathUtils.lerp(burnIn.scale, 1f, 1f - interpolated),
+                scaleClockOnly = useScaleOnly
+            )
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
index adc090d..8e8b09d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -19,9 +19,12 @@
 import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,11 +40,14 @@
 ) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = FromAodTransitionInteractor.TO_GONE_DURATION,
-            from = KeyguardState.AOD,
-            to = KeyguardState.GONE,
-        )
+        animationFlow
+            .setup(
+                duration = FromAodTransitionInteractor.TO_GONE_DURATION,
+                edge = Edge.create(from = AOD, to = Scenes.Gone),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = AOD, to = GONE),
+            )
 
     /**
      * AOD -> GONE should fade out the lockscreen contents. This transition plays both during wake
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index cbbb820..b267ecb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -19,9 +19,10 @@
 import android.util.MathUtils
 import com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -39,7 +40,6 @@
 class AodToLockscreenTransitionViewModel
 @Inject
 constructor(
-    deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     shadeInteractor: ShadeInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
@@ -47,8 +47,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_LOCKSCREEN_DURATION,
-            from = KeyguardState.AOD,
-            to = KeyguardState.LOCKSCREEN,
+            edge = Edge.create(from = AOD, to = LOCKSCREEN),
         )
 
     private var isShadeExpanded = false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
index 445575f..2497def 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -19,7 +19,9 @@
 import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -36,8 +38,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
-            from = KeyguardState.AOD,
-            to = KeyguardState.OCCLUDED,
+            edge = Edge.create(from = AOD, to = OCCLUDED),
         )
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
index 9a23007..35f05f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -37,11 +40,14 @@
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = FromAodTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
-            from = KeyguardState.AOD,
-            to = KeyguardState.PRIMARY_BOUNCER,
-        )
+        animationFlow
+            .setup(
+                duration = FromAodTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+                edge = Edge.create(from = AOD, to = Scenes.Bouncer),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = AOD, to = PRIMARY_BOUNCER),
+            )
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
index 570f377..caa0436 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
@@ -19,11 +19,13 @@
 import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.ScrimAlpha
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import dagger.Lazy
@@ -73,8 +75,12 @@
         return animationFlow
             .setup(
                 duration = duration,
-                from = from,
-                to = GONE,
+                // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene -> scene
+                //  transition
+                edge = Edge.create(from = from, to = Scenes.Gone)
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = from, to = GONE),
             )
             .sharedFlow(
                 duration = duration,
@@ -96,11 +102,16 @@
         var leaveShadeOpen: Boolean = false
         var willRunDismissFromKeyguard: Boolean = false
         val transitionAnimation =
-            animationFlow.setup(
-                duration = duration,
-                from = fromState,
-                to = GONE,
-            )
+            animationFlow
+                .setup(
+                    duration = duration,
+                    // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene ->
+                    //  scene transition
+                    edge = Edge.create(from = fromState, to = Scenes.Gone),
+                )
+                .setupWithoutSceneContainer(
+                    edge = Edge.create(from = fromState, to = GONE),
+                )
 
         return shadeInteractor.anyExpansion
             .map { it > 0f }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 87324a2..6f8389f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -117,7 +117,8 @@
                             KeyguardState.DOZING,
                             KeyguardState.DREAMING,
                             KeyguardState.PRIMARY_BOUNCER,
-                            KeyguardState.AOD -> emit(0f)
+                            KeyguardState.AOD,
+                            KeyguardState.UNDEFINED -> emit(0f)
                             KeyguardState.ALTERNATE_BOUNCER,
                             KeyguardState.LOCKSCREEN -> emit(1f)
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 53b2697..ae83c9e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -148,10 +148,11 @@
             KeyguardState.GLANCEABLE_HUB,
             KeyguardState.GONE,
             KeyguardState.OCCLUDED,
-            KeyguardState.DREAMING_LOCKSCREEN_HOSTED, -> 0f
+            KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+            KeyguardState.UNDEFINED, -> 0f
             KeyguardState.AOD,
             KeyguardState.ALTERNATE_BOUNCER,
-            KeyguardState.LOCKSCREEN -> 1f
+            KeyguardState.LOCKSCREEN, -> 1f
         }
     }
     val useBackgroundProtection: StateFlow<Boolean> = isUdfpsSupported
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
index 8851a51..77ebfce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
@@ -19,9 +19,12 @@
 import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GONE_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,11 +40,14 @@
 ) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_GONE_DURATION,
-            from = KeyguardState.DOZING,
-            to = KeyguardState.GONE,
-        )
+        animationFlow
+            .setup(
+                duration = TO_GONE_DURATION,
+                edge = Edge.create(from = DOZING, to = Scenes.Gone),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = DOZING, to = GONE),
+            )
 
     fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
         var startAlpha = 1f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
index 168d6e1..a460d51 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -18,7 +18,9 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -39,8 +41,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromDozingTransitionInteractor.TO_LOCKSCREEN_DURATION,
-            from = KeyguardState.DOZING,
-            to = KeyguardState.LOCKSCREEN,
+            edge = Edge.create(from = DOZING, to = LOCKSCREEN),
         )
 
     val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
index c0b1195..f33752f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
@@ -19,7 +19,9 @@
 import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -38,8 +40,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
-            from = KeyguardState.DOZING,
-            to = KeyguardState.OCCLUDED,
+            edge = Edge.create(from = DOZING, to = OCCLUDED),
         )
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
index 4395c34..7ddf641 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_PRIMARY_BOUNCER_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -38,11 +41,14 @@
 ) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_PRIMARY_BOUNCER_DURATION,
-            from = KeyguardState.DOZING,
-            to = KeyguardState.PRIMARY_BOUNCER,
-        )
+        animationFlow
+            .setup(
+                duration = TO_PRIMARY_BOUNCER_DURATION,
+                edge = Edge.create(from = DOZING, to = Scenes.Bouncer),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = DOZING, to = PRIMARY_BOUNCER),
+            )
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
index 67568e1..57ed455 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
@@ -18,7 +18,9 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -34,8 +36,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_LOCKSCREEN_DURATION,
-            from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-            to = KeyguardState.LOCKSCREEN,
+            edge = Edge.create(from = DREAMING_LOCKSCREEN_HOSTED, to = LOCKSCREEN),
         )
 
     val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
index 0fa7475..754ed6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
@@ -19,7 +19,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -40,12 +42,12 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromDreamingTransitionInteractor.TO_AOD_DURATION,
-            from = KeyguardState.DREAMING,
-            to = KeyguardState.AOD,
+            edge = Edge.create(from = DREAMING, to = AOD),
         )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
+
     override val deviceEntryParentViewAlpha: Flow<Float> =
         deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled
             ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
index 7468675..00aa102 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
@@ -19,9 +19,13 @@
 import com.android.app.animation.Interpolators.EMPHASIZED
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.Duration.Companion.seconds
@@ -37,13 +41,16 @@
 constructor(
     animationFlow: KeyguardTransitionAnimationFlow,
     configurationInteractor: ConfigurationInteractor,
-) {
+) : DeviceEntryIconTransition {
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_GLANCEABLE_HUB_DURATION,
-            from = KeyguardState.DREAMING,
-            to = KeyguardState.GLANCEABLE_HUB,
-        )
+        animationFlow
+            .setup(
+                duration = TO_GLANCEABLE_HUB_DURATION,
+                edge = Edge.create(from = DREAMING, to = Scenes.Communal),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = DREAMING, to = GLANCEABLE_HUB),
+            )
 
     val dreamOverlayTranslationX: Flow<Float> =
         configurationInteractor
@@ -79,6 +86,15 @@
             )
             .map { step -> step != 0f }
 
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.sharedFlow(
+            startTime = 167.milliseconds,
+            duration = 167.milliseconds,
+            onStep = { it },
+            onCancel = { 0f },
+            onFinish = { 1f },
+        )
+
     private companion object {
         val TO_GLANCEABLE_HUB_DURATION = 1.seconds
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt
index ec7b931..1bdf6d29 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt
@@ -18,10 +18,13 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
-import kotlinx.coroutines.flow.Flow
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
 
 @SysUISingleton
 class DreamingToGoneTransitionViewModel
@@ -31,13 +34,15 @@
 ) {
 
     private val transitionAnimation =
-            animationFlow.setup(
+        animationFlow
+            .setup(
                 duration = FromDreamingTransitionInteractor.TO_GONE_DURATION,
-                from = KeyguardState.DREAMING,
-                to = KeyguardState.GONE,
+                edge = Edge.create(from = DREAMING, to = Scenes.Gone),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = DREAMING, to = GONE),
             )
 
     /** Lockscreen views alpha */
     val lockscreenAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f)
-
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index f191aa7..82381eb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -20,7 +20,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -45,8 +47,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_LOCKSCREEN_DURATION,
-            from = KeyguardState.DREAMING,
-            to = KeyguardState.LOCKSCREEN,
+            edge = Edge.create(from = DREAMING, to = LOCKSCREEN),
         )
 
     /** Dream overlay y-translation on exit */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
index 838c22b..d594488 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
@@ -19,9 +19,13 @@
 import com.android.app.animation.Interpolators
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.Duration.Companion.seconds
@@ -37,14 +41,17 @@
 constructor(
     animationFlow: KeyguardTransitionAnimationFlow,
     configurationInteractor: ConfigurationInteractor,
-) {
+) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = FROM_GLANCEABLE_HUB_DURATION,
-            from = KeyguardState.GLANCEABLE_HUB,
-            to = KeyguardState.DREAMING,
-        )
+        animationFlow
+            .setup(
+                duration = FROM_GLANCEABLE_HUB_DURATION,
+                edge = Edge.create(from = Scenes.Communal, to = DREAMING),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = GLANCEABLE_HUB, to = DREAMING),
+            )
 
     val dreamOverlayAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
@@ -78,6 +85,14 @@
             )
             .map { step -> step != 1f }
 
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.sharedFlow(
+            duration = 167.milliseconds,
+            onStep = { 1 - it },
+            onCancel = { 1f },
+            onFinish = { 0f },
+        )
+
     private companion object {
         val FROM_GLANCEABLE_HUB_DURATION = 1.seconds
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
index e05b500..046b95f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
@@ -20,10 +20,13 @@
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,11 +48,14 @@
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_LOCKSCREEN_DURATION,
-            from = KeyguardState.GLANCEABLE_HUB,
-            to = KeyguardState.LOCKSCREEN,
-        )
+        animationFlow
+            .setup(
+                duration = TO_LOCKSCREEN_DURATION,
+                edge = Edge.create(from = Scenes.Communal, to = LOCKSCREEN),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = GLANCEABLE_HUB, to = LOCKSCREEN),
+            )
 
     val keyguardAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt
new file mode 100644
index 0000000..cd98bb0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_OCCLUDED_DURATION
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class GlanceableHubToOccludedTransitionViewModel
+@Inject
+constructor(
+    animationFlow: KeyguardTransitionAnimationFlow,
+) : DeviceEntryIconTransition {
+
+    private val transitionAnimation =
+        animationFlow
+            .setup(
+                duration = TO_OCCLUDED_DURATION,
+                edge = Edge.create(from = Scenes.Communal, to = OCCLUDED),
+            )
+            .setupWithoutSceneContainer(edge = Edge.create(from = GLANCEABLE_HUB, to = OCCLUDED))
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 3540bec..74f7d75 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -20,10 +20,13 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,11 +45,14 @@
 ) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_AOD_DURATION,
-            from = KeyguardState.GONE,
-            to = KeyguardState.AOD,
-        )
+        animationFlow
+            .setup(
+                duration = TO_AOD_DURATION,
+                edge = Edge.create(from = Scenes.Gone, to = AOD),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = GONE, to = AOD),
+            )
 
     /** y-translation from the top of the screen for AOD */
     fun enterFromTopTranslationY(translatePx: Int): Flow<StateToValue> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
index 80a6bda..70c0032 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
@@ -19,9 +19,12 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DOZING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -40,11 +43,14 @@
 ) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_DOZING_DURATION,
-            from = KeyguardState.GONE,
-            to = KeyguardState.DOZING,
-        )
+        animationFlow
+            .setup(
+                duration = TO_DOZING_DURATION,
+                edge = Edge.create(from = Scenes.Gone, to = DOZING),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = GONE, to = DOZING),
+            )
 
     val lockscreenAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
index b527463..627f0de 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
@@ -18,8 +18,11 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.Flow
@@ -36,11 +39,14 @@
 ) {
 
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_DREAMING_DURATION,
-            from = KeyguardState.GONE,
-            to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-        )
+        animationFlow
+            .setup(
+                duration = TO_DREAMING_DURATION,
+                edge = Edge.create(from = Scenes.Gone, to = DREAMING_LOCKSCREEN_HOSTED),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = GONE, to = DREAMING_LOCKSCREEN_HOSTED),
+            )
 
     /** Lockscreen views alpha - hide immediately */
     val lockscreenAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
index 102242a..f8b6e28 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
@@ -19,8 +19,11 @@
 import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.Flow
@@ -34,11 +37,14 @@
 ) {
 
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_DREAMING_DURATION,
-            from = KeyguardState.GONE,
-            to = KeyguardState.DREAMING,
-        )
+        animationFlow
+            .setup(
+                duration = TO_DREAMING_DURATION,
+                edge = Edge.create(from = Scenes.Gone, to = DREAMING),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = GONE, to = DREAMING),
+            )
 
     /** Lockscreen views y-translation */
     fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
index a2ce408..08ec43f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
@@ -18,9 +18,12 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.Flow
@@ -33,11 +36,14 @@
 ) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_LOCKSCREEN_DURATION,
-            from = KeyguardState.GONE,
-            to = KeyguardState.LOCKSCREEN
-        )
+        animationFlow
+            .setup(
+                duration = TO_LOCKSCREEN_DURATION,
+                edge = Edge.create(from = Scenes.Gone, to = LOCKSCREEN),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = GONE, to = LOCKSCREEN),
+            )
 
     val shortcutsAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index bbcea56..f405b9d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.BurnInModel
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -38,6 +39,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
 import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
 import com.android.systemui.keyguard.ui.StateToValue
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 import com.android.systemui.statusbar.phone.DozeParameters
@@ -115,14 +117,18 @@
     private val shadeInteractor: ShadeInteractor,
 ) {
     private var burnInJob: Job? = null
-    private val burnInModel = MutableStateFlow(BurnInModel())
+    internal val burnInModel = MutableStateFlow(BurnInModel())
 
     val burnInLayerVisibility: Flow<Int> =
         keyguardTransitionInteractor.startedKeyguardState
             .filter { it == AOD || it == LOCKSCREEN }
             .map { VISIBLE }
 
-    val goneToAodTransition = keyguardTransitionInteractor.transition(from = GONE, to = AOD)
+    val goneToAodTransition =
+        keyguardTransitionInteractor.transition(
+            edge = Edge.create(Scenes.Gone, AOD),
+            edgeWithoutSceneContainer = Edge.create(GONE, AOD)
+        )
 
     private val goneToAodTransitionRunning: Flow<Boolean> =
         goneToAodTransition
@@ -144,7 +150,10 @@
 
     private val alphaOnShadeExpansion: Flow<Float> =
         combineTransform(
-                keyguardTransitionInteractor.isInTransition(from = LOCKSCREEN, to = GONE),
+                keyguardTransitionInteractor.isInTransition(
+                    edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone),
+                    edgeWithoutSceneContainer = Edge.create(from = LOCKSCREEN, to = GONE),
+                ),
                 isOnLockscreen,
                 shadeInteractor.qsExpansion,
                 shadeInteractor.shadeExpansion,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index d8b5013..02e48fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
@@ -89,10 +90,15 @@
         shadeMode: ShadeMode,
     ): Map<UserAction, UserActionResult> {
         val shadeSceneKey =
-            if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade
+            UserActionResult(
+                toScene =
+                    if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade,
+                transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split },
+            )
 
         val quickSettingsIfSingleShade =
-            if (shadeMode is ShadeMode.Single) Scenes.QuickSettings else shadeSceneKey
+            if (shadeMode is ShadeMode.Single) UserActionResult(Scenes.QuickSettings)
+            else shadeSceneKey
 
         return mapOf(
                 Swipe.Left to UserActionResult(Scenes.Communal).takeIf { isCommunalAvailable },
@@ -103,7 +109,7 @@
                 swipeDownFromTop(pointerCount = 2) to
                     // TODO(b/338577208): Remove 'Dual' once we add Dual Shade invocation zones.
                     if (shadeMode is ShadeMode.Dual) {
-                        Scenes.QuickSettingsShade
+                        UserActionResult(Scenes.QuickSettingsShade)
                     } else {
                         quickSettingsIfSingleShade
                     },
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 1f9f304..8b5b347 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -20,7 +20,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -45,8 +47,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromLockscreenTransitionInteractor.TO_AOD_DURATION,
-            from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.AOD,
+            edge = Edge.create(from = LOCKSCREEN, to = AOD),
         )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index c836f01..27a1f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -19,7 +19,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DOZING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -40,8 +42,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_DOZING_DURATION,
-            from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.DOZING,
+            edge = Edge.create(from = LOCKSCREEN, to = DOZING),
         )
 
     val lockscreenAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
index 19b9cf47..778dbed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
@@ -18,7 +18,9 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -34,8 +36,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_DREAMING_HOSTED_DURATION,
-            from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+            edge = Edge.create(from = LOCKSCREEN, to = DREAMING_LOCKSCREEN_HOSTED),
         )
 
     val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index 13522a6..579abeb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -19,7 +19,9 @@
 import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -40,8 +42,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_DREAMING_DURATION,
-            from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.DREAMING,
+            edge = Edge.create(from = LOCKSCREEN, to = DREAMING),
         )
 
     /** Lockscreen views y-translation */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
index dae7897..c7273b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
@@ -20,10 +20,13 @@
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,11 +48,14 @@
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
-            from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.GLANCEABLE_HUB,
-        )
+        animationFlow
+            .setup(
+                duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
+                edge = Edge.create(from = LOCKSCREEN, to = Scenes.Communal),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = LOCKSCREEN, to = GLANCEABLE_HUB),
+            )
 
     val keyguardAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
index f03625e..1314e88 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -19,10 +19,13 @@
 import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow.FlowBuilder
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -42,11 +45,14 @@
 ) : DeviceEntryIconTransition {
 
     private val transitionAnimation: FlowBuilder =
-        animationFlow.setup(
-            duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION,
-            from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.GONE,
-        )
+        animationFlow
+            .setup(
+                duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION,
+                edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = LOCKSCREEN, to = GONE),
+            )
 
     val shortcutsAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index dd6652e..fcf8c14f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -20,7 +20,9 @@
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.res.R
@@ -45,8 +47,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_OCCLUDED_DURATION,
-            from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.OCCLUDED,
+            edge = Edge.create(from = KeyguardState.LOCKSCREEN, to = OCCLUDED),
         )
 
     /** Lockscreen views alpha */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index 0cfc757..23c44b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -39,11 +42,14 @@
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
-            from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.PRIMARY_BOUNCER,
-        )
+        animationFlow
+            .setup(
+                duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+                edge = Edge.create(from = LOCKSCREEN, to = Scenes.Bouncer),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = LOCKSCREEN, to = PRIMARY_BOUNCER),
+            )
 
     val shortcutsAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
index d7ba46b..706a3c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
@@ -19,7 +19,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -41,8 +43,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromOccludedTransitionInteractor.TO_AOD_DURATION,
-            from = KeyguardState.OCCLUDED,
-            to = KeyguardState.AOD,
+            edge = Edge.create(from = OCCLUDED, to = AOD),
         )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
index 91554e3..af01930 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
@@ -18,7 +18,9 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -38,8 +40,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromOccludedTransitionInteractor.TO_DOZING_DURATION,
-            from = KeyguardState.OCCLUDED,
-            to = KeyguardState.DOZING,
+            edge = Edge.create(from = OCCLUDED, to = DOZING),
         )
 
     /** Lockscreen views alpha */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt
new file mode 100644
index 0000000..47e202b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class OccludedToGlanceableHubTransitionViewModel
+@Inject
+constructor(
+    animationFlow: KeyguardTransitionAnimationFlow,
+) : DeviceEntryIconTransition {
+
+    private val transitionAnimation =
+        animationFlow
+            .setup(
+                duration = TO_GLANCEABLE_HUB_DURATION,
+                edge = Edge.create(OCCLUDED, Scenes.Communal)
+            )
+            .setupWithoutSceneContainer(edge = Edge.create(OCCLUDED, GLANCEABLE_HUB))
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
index d2c9cfb..98dba39 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
@@ -17,8 +17,11 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,11 +36,14 @@
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = DEFAULT_DURATION,
-            from = KeyguardState.OCCLUDED,
-            to = KeyguardState.GONE,
-        )
+        animationFlow
+            .setup(
+                duration = DEFAULT_DURATION,
+                edge = Edge.create(from = OCCLUDED, to = Scenes.Gone),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = OCCLUDED, to = GONE),
+            )
 
     fun notificationAlpha(viewState: ViewStateAccessor): Flow<Float> {
         var currentAlpha = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index a09d58a..36c7d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -23,7 +23,9 @@
 import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.res.R
@@ -56,8 +58,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_LOCKSCREEN_DURATION,
-            from = KeyguardState.OCCLUDED,
-            to = KeyguardState.LOCKSCREEN,
+            edge = Edge.create(from = OCCLUDED, to = LOCKSCREEN),
         )
 
     /** Lockscreen views y-translation */
@@ -101,7 +102,7 @@
                 .filter { (wasOccluded, isOccluded) ->
                     wasOccluded &&
                         !isOccluded &&
-                        keyguardTransitionInteractor.getCurrentState() == KeyguardState.OCCLUDED
+                        keyguardTransitionInteractor.getCurrentState() == OCCLUDED
                 }
                 .map { 0f }
         )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
index cf6a533..1eecbd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
@@ -17,7 +17,9 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -34,8 +36,7 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = 250.milliseconds,
-            from = KeyguardState.OFF,
-            to = KeyguardState.LOCKSCREEN,
+            edge = Edge.create(from = OFF, to = LOCKSCREEN),
         )
 
     val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
index 942903b..009f85d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -19,9 +19,12 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,11 +45,14 @@
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
-            from = KeyguardState.PRIMARY_BOUNCER,
-            to = KeyguardState.AOD,
-        )
+        animationFlow
+            .setup(
+                duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
+                edge = Edge.create(from = Scenes.Bouncer, to = AOD),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = PRIMARY_BOUNCER, to = AOD),
+            )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
index 13f651a..e5bb464 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
@@ -19,9 +19,12 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_DOZING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -42,11 +45,14 @@
 ) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_DOZING_DURATION,
-            from = KeyguardState.PRIMARY_BOUNCER,
-            to = KeyguardState.DOZING,
-        )
+        animationFlow
+            .setup(
+                duration = TO_DOZING_DURATION,
+                edge = Edge.create(from = Scenes.Bouncer, to = DOZING),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = PRIMARY_BOUNCER, to = DOZING),
+            )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index b1fa710..7ae4558 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -20,11 +20,13 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.ScrimAlpha
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import dagger.Lazy
 import javax.inject.Inject
@@ -49,11 +51,14 @@
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_GONE_DURATION,
-            from = PRIMARY_BOUNCER,
-            to = GONE,
-        )
+        animationFlow
+            .setup(
+                duration = TO_GONE_DURATION,
+                edge = Edge.create(from = PRIMARY_BOUNCER, to = Scenes.Gone),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = PRIMARY_BOUNCER, to = GONE),
+            )
 
     private var leaveShadeOpen: Boolean = false
     private var willRunDismissFromKeyguard: Boolean = false
@@ -88,6 +93,7 @@
         } else {
             createBouncerAlphaFlow(primaryBouncerInteractor::willRunDismissFromKeyguard)
         }
+
     private fun createBouncerAlphaFlow(willRunAnimationOnKeyguard: () -> Boolean): Flow<Float> {
         return transitionAnimation.sharedFlow(
             duration = 200.milliseconds,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index 2575041..7511101 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -19,9 +19,12 @@
 import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -39,11 +42,14 @@
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
-        animationFlow.setup(
-            duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
-            from = KeyguardState.PRIMARY_BOUNCER,
-            to = KeyguardState.LOCKSCREEN,
-        )
+        animationFlow
+            .setup(
+                duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
+                edge = Edge.create(from = Scenes.Bouncer, to = LOCKSCREEN),
+            )
+            .setupWithoutSceneContainer(
+                edge = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN),
+            )
 
     val shortcutsAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
index 9719c02..0c70f10 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
@@ -107,14 +107,11 @@
             .thenByDescending { it.updateTime }
             .thenByDescending { it.notificationKey }
 
-    private val _sortedMedia: MutableStateFlow<TreeMap<MediaSortKeyModel, MediaCommonModel>> =
-        MutableStateFlow(TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator))
-    val sortedMedia: StateFlow<Map<MediaSortKeyModel, MediaCommonModel>> =
-        _sortedMedia.asStateFlow()
+    private val _currentMedia: MutableStateFlow<List<MediaCommonModel>> =
+        MutableStateFlow(mutableListOf())
+    val currentMedia = _currentMedia.asStateFlow()
 
-    private val _isMediaFromRec: MutableStateFlow<Boolean> = MutableStateFlow(false)
-    val isMediaFromRec: StateFlow<Boolean> = _isMediaFromRec.asStateFlow()
-
+    private var sortedMedia = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
     private var mediaFromRecPackageName: String? = null
     private var locale: Locale = applicationContext.resources.configuration.locales.get(0)
 
@@ -186,7 +183,7 @@
     fun addMediaDataLoadingState(mediaDataLoadingModel: MediaDataLoadingModel) {
         val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
         sortedMap.putAll(
-            _sortedMedia.value.filter { (_, commonModel) ->
+            sortedMedia.filter { (_, commonModel) ->
                 commonModel !is MediaCommonModel.MediaControl ||
                     commonModel.mediaLoadedModel.instanceId != mediaDataLoadingModel.instanceId
             }
@@ -207,18 +204,52 @@
                 )
 
             if (mediaDataLoadingModel is MediaDataLoadingModel.Loaded) {
-                val isMediaFromRec = isMediaFromRec(it)
+                val newCommonModel =
+                    MediaCommonModel.MediaControl(
+                        mediaDataLoadingModel,
+                        canBeRemoved(it),
+                        isMediaFromRec(it)
+                    )
+                sortedMap[sortKey] = newCommonModel
 
-                _isMediaFromRec.value = isMediaFromRec
-                if (isMediaFromRec) {
-                    mediaFromRecPackageName = null
+                // On Addition or tapping on recommendations, we should show the new order of media.
+                if (mediaFromRecPackageName == it.packageName) {
+                    if (it.isPlaying == true) {
+                        mediaFromRecPackageName = null
+                        _currentMedia.value = sortedMap.values.toList()
+                    }
+                } else if (sortedMap.size > sortedMedia.size) {
+                    _currentMedia.value = sortedMap.values.toList()
+                } else if (sortedMap.size == sortedMedia.size) {
+                    // When loading an update for an existing media control.
+                    val currentList =
+                        mutableListOf<MediaCommonModel>().apply { addAll(_currentMedia.value) }
+                    currentList.forEachIndexed { index, mediaCommonModel ->
+                        if (
+                            mediaCommonModel is MediaCommonModel.MediaControl &&
+                                mediaCommonModel.mediaLoadedModel.instanceId ==
+                                    mediaDataLoadingModel.instanceId &&
+                                mediaCommonModel != newCommonModel
+                        ) {
+                            // Update media model if changed.
+                            currentList[index] = newCommonModel
+                        }
+                    }
+                    _currentMedia.value = currentList
                 }
-                sortedMap[sortKey] =
-                    MediaCommonModel.MediaControl(mediaDataLoadingModel, canBeRemoved(it))
             }
         }
 
-        _sortedMedia.value = sortedMap
+        sortedMedia = sortedMap
+
+        // On removal we want to keep the order being shown to user.
+        if (mediaDataLoadingModel is MediaDataLoadingModel.Removed) {
+            _currentMedia.value =
+                _currentMedia.value.filter { commonModel ->
+                    commonModel !is MediaCommonModel.MediaControl ||
+                        mediaDataLoadingModel.instanceId != commonModel.mediaLoadedModel.instanceId
+                }
+        }
     }
 
     fun setRecommendationsLoadingState(smartspaceMediaLoadingModel: SmartspaceMediaLoadingModel) {
@@ -229,7 +260,7 @@
             }
         val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
         sortedMap.putAll(
-            _sortedMedia.value.filter { (_, commonModel) ->
+            sortedMedia.filter { (_, commonModel) ->
                 commonModel !is MediaCommonModel.MediaRecommendations
             }
         )
@@ -240,11 +271,25 @@
                 isPlaying = false,
                 active = _smartspaceMediaData.value.isActive,
             )
-        if (smartspaceMediaLoadingModel is SmartspaceMediaLoadingModel.Loaded) {
-            sortedMap[sortKey] = MediaCommonModel.MediaRecommendations(smartspaceMediaLoadingModel)
+        when (smartspaceMediaLoadingModel) {
+            is SmartspaceMediaLoadingModel.Loaded ->
+                sortedMap[sortKey] =
+                    MediaCommonModel.MediaRecommendations(smartspaceMediaLoadingModel)
+            is SmartspaceMediaLoadingModel.Removed ->
+                _currentMedia.value =
+                    _currentMedia.value.filter { commonModel ->
+                        commonModel !is MediaCommonModel.MediaRecommendations
+                    }
         }
 
-        _sortedMedia.value = sortedMap
+        if (sortedMap.size > sortedMedia.size) {
+            _currentMedia.value = sortedMap.values.toList()
+        }
+        sortedMedia = sortedMap
+    }
+
+    fun setOrderedMedia() {
+        _currentMedia.value = sortedMedia.values.toList()
     }
 
     fun setMediaFromRecPackageName(packageName: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt
index c02478b..96ef7d2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt
@@ -206,11 +206,11 @@
         listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
     }
 
-    override fun onMediaDataRemoved(key: String) {
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
         allEntries.remove(key)
         userEntries.remove(key)?.let {
             // Only notify listeners if something actually changed
-            listeners.forEach { it.onMediaDataRemoved(key) }
+            listeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
         }
     }
 
@@ -246,7 +246,7 @@
                 // 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) }
+                listeners.forEach { listener -> listener.onMediaDataRemoved(key, false) }
             }
         }
     }
@@ -261,7 +261,7 @@
         userEntries.clear()
         keyCopy.forEach {
             if (DEBUG) Log.d(TAG, "Removing $it after user change")
-            listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) }
+            listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it, false) }
         }
 
         allEntries.forEach { (key, data) ->
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
index 3a831156..143d66b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
@@ -545,8 +545,8 @@
      * External listeners registered with [addListener] will be notified after the event propagates
      * through the internal listener pipeline.
      */
-    private fun notifyMediaDataRemoved(key: String) {
-        internalListeners.forEach { it.onMediaDataRemoved(key) }
+    private fun notifyMediaDataRemoved(key: String, userInitiated: Boolean = false) {
+        internalListeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
     }
 
     /**
@@ -578,7 +578,7 @@
             if (it.active == !timedOut && !forceUpdate) {
                 if (it.resumption) {
                     if (DEBUG) Log.d(TAG, "timing out resume player $key")
-                    dismissMediaData(key, 0L /* delay */)
+                    dismissMediaData(key, delay = 0L, userInitiated = false)
                 }
                 return
             }
@@ -627,17 +627,17 @@
         }
     }
 
-    private fun removeEntry(key: String, logEvent: Boolean = true) {
+    private fun removeEntry(key: String, logEvent: Boolean = true, userInitiated: Boolean = false) {
         mediaEntries.remove(key)?.let {
             if (logEvent) {
                 logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
             }
         }
-        notifyMediaDataRemoved(key)
+        notifyMediaDataRemoved(key, userInitiated)
     }
 
     /** Dismiss a media entry. Returns false if the key was not found. */
-    override fun dismissMediaData(key: String, delay: Long): Boolean {
+    override fun dismissMediaData(key: String, delay: Long, userInitiated: Boolean): Boolean {
         val existed = mediaEntries[key] != null
         backgroundExecutor.execute {
             mediaEntries[key]?.let { mediaData ->
@@ -649,7 +649,10 @@
                 }
             }
         }
-        foregroundExecutor.executeDelayed({ removeEntry(key) }, delay)
+        foregroundExecutor.executeDelayed(
+            { removeEntry(key = key, userInitiated = userInitiated) },
+            delay
+        )
         return existed
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
index ad70db5..88910f9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
@@ -53,8 +53,8 @@
         listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, data) }
     }
 
-    override fun onMediaDataRemoved(key: String) {
-        remove(key)
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
+        remove(key, userInitiated)
     }
 
     override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
@@ -71,8 +71,8 @@
         }
     }
 
-    override fun onKeyRemoved(key: String) {
-        remove(key)
+    override fun onKeyRemoved(key: String, userInitiated: Boolean) {
+        remove(key, userInitiated)
     }
 
     /**
@@ -92,10 +92,10 @@
         }
     }
 
-    private fun remove(key: String) {
+    private fun remove(key: String, userInitiated: Boolean) {
         entries.remove(key)?.let {
             val listenersCopy = listeners.toSet()
-            listenersCopy.forEach { it.onMediaDataRemoved(key) }
+            listenersCopy.forEach { it.onMediaDataRemoved(key, userInitiated) }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index 5432a18..8d19ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -213,7 +213,7 @@
         listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
     }
 
-    override fun onMediaDataRemoved(key: String) {
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
         mediaFilterRepository.removeMediaEntry(key)?.let { mediaData ->
             val instanceId = mediaData.instanceId
             mediaFilterRepository.removeSelectedUserMediaEntry(instanceId)?.let {
@@ -221,7 +221,7 @@
                     MediaDataLoadingModel.Removed(instanceId)
                 )
                 // Only notify listeners if something actually changed
-                listeners.forEach { it.onMediaDataRemoved(key) }
+                listeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
             }
         }
     }
@@ -270,7 +270,7 @@
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Removed(data.instanceId)
                 )
-                listeners.forEach { listener -> listener.onMediaDataRemoved(key) }
+                listeners.forEach { listener -> listener.onMediaDataRemoved(key, false) }
             }
         }
     }
@@ -288,7 +288,7 @@
                 MediaDataLoadingModel.Removed(instanceId)
             )
             getKey(instanceId)?.let {
-                listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) }
+                listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it, false) }
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
index 2331aa21..8099e59 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
@@ -60,7 +60,7 @@
     )
 
     /** Dismiss a media entry. Returns false if the key was not found. */
-    fun dismissMediaData(key: String, delay: Long): Boolean
+    fun dismissMediaData(key: String, delay: Long, userInitiated: Boolean): Boolean
 
     /**
      * Called whenever the recommendation has been expired or removed by the user. This will remove
@@ -136,7 +136,7 @@
         ) {}
 
         /** Called whenever a previously existing Media notification was removed. */
-        override fun onMediaDataRemoved(key: String) {}
+        override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {}
 
         /**
          * Called whenever a previously existing Smartspace media data was removed.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index 1d7c025..eed7752 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -498,8 +498,8 @@
      * External listeners registered with [MediaCarouselInteractor.addListener] will be notified
      * after the event propagates through the internal listener pipeline.
      */
-    private fun notifyMediaDataRemoved(key: String) {
-        internalListeners.forEach { it.onMediaDataRemoved(key) }
+    private fun notifyMediaDataRemoved(key: String, userInitiated: Boolean = false) {
+        internalListeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
     }
 
     /**
@@ -531,7 +531,7 @@
             if (it.active == !timedOut && !forceUpdate) {
                 if (it.resumption) {
                     if (DEBUG) Log.d(TAG, "timing out resume player $key")
-                    dismissMediaData(key, 0L /* delay */)
+                    dismissMediaData(key, delayMs = 0L, userInitiated = false)
                 }
                 return
             }
@@ -580,17 +580,17 @@
         }
     }
 
-    private fun removeEntry(key: String, logEvent: Boolean = true) {
+    private fun removeEntry(key: String, logEvent: Boolean = true, userInitiated: Boolean = false) {
         mediaDataRepository.removeMediaEntry(key)?.let {
             if (logEvent) {
                 logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
             }
         }
-        notifyMediaDataRemoved(key)
+        notifyMediaDataRemoved(key, userInitiated)
     }
 
     /** Dismiss a media entry. Returns false if the key was not found. */
-    fun dismissMediaData(key: String, delayMs: Long): Boolean {
+    fun dismissMediaData(key: String, delayMs: Long, userInitiated: Boolean): Boolean {
         val existed = mediaDataRepository.mediaEntries.value[key] != null
         backgroundExecutor.execute {
             mediaDataRepository.mediaEntries.value[key]?.let { mediaData ->
@@ -602,16 +602,19 @@
                 }
             }
         }
-        foregroundExecutor.executeDelayed({ removeEntry(key) }, delayMs)
+        foregroundExecutor.executeDelayed(
+            { removeEntry(key, userInitiated = userInitiated) },
+            delayMs
+        )
         return existed
     }
 
     /** Dismiss a media entry. Returns false if the corresponding key was not found. */
-    fun dismissMediaData(instanceId: InstanceId, delayMs: Long): Boolean {
+    fun dismissMediaData(instanceId: InstanceId, delayMs: Long, userInitiated: Boolean): Boolean {
         val mediaEntries = mediaDataRepository.mediaEntries.value
         val filteredEntries = mediaEntries.filter { (_, data) -> data.instanceId == instanceId }
         return if (filteredEntries.isNotEmpty()) {
-            dismissMediaData(filteredEntries.keys.first(), delayMs)
+            dismissMediaData(filteredEntries.keys.first(), delayMs, userInitiated)
         } else {
             false
         }
@@ -1579,7 +1582,7 @@
         ) {}
 
         /** Called whenever a previously existing Media notification was removed. */
-        fun onMediaDataRemoved(key: String) {}
+        fun onMediaDataRemoved(key: String, userInitiated: Boolean) {}
 
         /**
          * Called whenever a previously existing Smartspace media data was removed.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index 0e2814b..043fbfa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -111,10 +111,10 @@
         }
     }
 
-    override fun onMediaDataRemoved(key: String) {
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
         val token = entries.remove(key)
         token?.stop()
-        token?.let { listeners.forEach { it.onKeyRemoved(key) } }
+        token?.let { listeners.forEach { it.onKeyRemoved(key, userInitiated) } }
     }
 
     fun dump(pw: PrintWriter) {
@@ -136,7 +136,7 @@
         /** Called when the route has changed for a given notification. */
         fun onMediaDeviceChanged(key: String, oldKey: String?, data: MediaDeviceData?)
         /** Called when the notification was removed. */
-        fun onKeyRemoved(key: String)
+        fun onKeyRemoved(key: String, userInitiated: Boolean)
     }
 
     private inner class Entry(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
index b2a8f2e..b178d84 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
@@ -137,7 +137,7 @@
                 // farther and dismiss the media data so that media controls for the local session
                 // don't hang around while casting.
                 if (!keyedTokens.get(key)!!.contains(TokenId(remote.sessionToken))) {
-                    dispatchMediaDataRemoved(key)
+                    dispatchMediaDataRemoved(key, userInitiated = false)
                 }
             }
         }
@@ -151,11 +151,11 @@
         backgroundExecutor.execute { dispatchSmartspaceMediaDataLoaded(key, data) }
     }
 
-    override fun onMediaDataRemoved(key: String) {
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
         // Queue on background thread to ensure ordering of loaded and removed events is maintained.
         backgroundExecutor.execute {
             keyedTokens.remove(key)
-            dispatchMediaDataRemoved(key)
+            dispatchMediaDataRemoved(key, userInitiated)
         }
     }
 
@@ -174,8 +174,10 @@
         }
     }
 
-    private fun dispatchMediaDataRemoved(key: String) {
-        foregroundExecutor.execute { listeners.toSet().forEach { it.onMediaDataRemoved(key) } }
+    private fun dispatchMediaDataRemoved(key: String, userInitiated: Boolean) {
+        foregroundExecutor.execute {
+            listeners.toSet().forEach { it.onMediaDataRemoved(key, userInitiated) }
+        }
     }
 
     private fun dispatchSmartspaceMediaDataLoaded(key: String, info: SmartspaceMediaData) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
index 29f3967..fc31903 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
@@ -169,7 +169,7 @@
         mediaListeners[key] = PlaybackStateListener(key, data)
     }
 
-    override fun onMediaDataRemoved(key: String) {
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
         mediaListeners.remove(key)?.destroy()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index 33c0b19..9e62300 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -63,7 +63,7 @@
     private val mediaDeviceManager: MediaDeviceManager,
     private val mediaDataCombineLatest: MediaDataCombineLatest,
     private val mediaDataFilter: MediaDataFilterImpl,
-    mediaFilterRepository: MediaFilterRepository,
+    private val mediaFilterRepository: MediaFilterRepository,
     private val mediaFlags: MediaFlags,
 ) : MediaDataManager, CoreStartable {
 
@@ -123,18 +123,8 @@
                 initialValue = false,
             )
 
-    /** The most recent sorted set for user media instances */
-    val sortedMedia: StateFlow<List<MediaCommonModel>> =
-        mediaFilterRepository.sortedMedia
-            .mapLatest { it.values.toList() }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = emptyList(),
-            )
-
-    /** Whether the current change in media was done by clicking on a recommendation */
-    val isMediaFromRec: StateFlow<Boolean> = mediaFilterRepository.isMediaFromRec
+    /** The current list for user media instances */
+    val currentMedia: StateFlow<List<MediaCommonModel>> = mediaFilterRepository.currentMedia
 
     override fun start() {
         if (!mediaFlags.isMediaControlsRefactorEnabled()) {
@@ -215,12 +205,12 @@
         )
     }
 
-    override fun dismissMediaData(key: String, delay: Long): Boolean {
-        return mediaDataProcessor.dismissMediaData(key, delay)
+    override fun dismissMediaData(key: String, delay: Long, userInitiated: Boolean): Boolean {
+        return mediaDataProcessor.dismissMediaData(key, delay, userInitiated)
     }
 
     fun removeMediaControl(instanceId: InstanceId, delay: Long) {
-        mediaDataProcessor.dismissMediaData(instanceId, delay)
+        mediaDataProcessor.dismissMediaData(instanceId, delay, userInitiated = false)
     }
 
     override fun dismissSmartspaceRecommendation(key: String, delay: Long) {
@@ -251,6 +241,10 @@
 
     override fun isRecommendationActive() = mediaDataRepository.smartspaceMediaData.value.isActive
 
+    fun reorderMedia() {
+        mediaFilterRepository.setOrderedMedia()
+    }
+
     /** Add a listener for internal events. */
     private fun addInternalListener(listener: MediaDataManager.Listener) =
         mediaDataProcessor.addInternalListener(listener)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index 9f2d132..1a0f582 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -21,9 +21,7 @@
 import android.app.PendingIntent
 import android.content.Context
 import android.content.Intent
-import android.media.session.MediaController
 import android.media.session.MediaSession
-import android.media.session.PlaybackState
 import android.provider.Settings
 import android.util.Log
 import com.android.internal.jank.Cuj
@@ -42,7 +40,6 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.kotlin.pairwiseBy
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.flow.Flow
@@ -70,19 +67,6 @@
             .map { entries -> entries[instanceId]?.let { toMediaControlModel(it) } }
             .distinctUntilChanged()
 
-    val isStartedPlaying: Flow<Boolean> =
-        mediaControl
-            .map { mediaControl ->
-                mediaControl?.token?.let { token ->
-                    MediaController(applicationContext, token).playbackState?.let {
-                        it.state == PlaybackState.STATE_PLAYING
-                    }
-                }
-                    ?: false
-            }
-            .pairwiseBy(initialValue = false) { wasPlaying, isPlaying -> !wasPlaying && isPlaying }
-            .distinctUntilChanged()
-
     val onAnyMediaConfigurationChange: Flow<Unit> = repository.onAnyMediaConfigurationChange
 
     fun removeMediaControl(
@@ -90,7 +74,8 @@
         instanceId: InstanceId,
         delayMs: Long
     ): Boolean {
-        val dismissed = mediaDataProcessor.dismissMediaData(instanceId, delayMs)
+        val dismissed =
+            mediaDataProcessor.dismissMediaData(instanceId, delayMs, userInitiated = true)
         if (!dismissed) {
             Log.w(
                 TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
index 23860bb..56cc618 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
@@ -21,6 +21,7 @@
     data class MediaControl(
         val mediaLoadedModel: MediaDataLoadingModel.Loaded,
         val canBeRemoved: Boolean = false,
+        val isMediaFromRec: Boolean = false,
     ) : MediaCommonModel()
 
     data class MediaRecommendations(val recsLoadingModel: SmartspaceMediaLoadingModel) :
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 73fb558..fed93f0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -260,44 +260,50 @@
             }
 
             SEMANTIC_ACTIONS_ALL.forEachIndexed { index, id ->
-                val button = viewHolder.getAction(id)
-                val actionViewModel = viewModel.actionButtons[index]
-                if (button.id == R.id.actionPrev) {
-                    actionViewModel?.let {
-                        viewController.setUpPrevButtonInfo(true, it.notVisibleValue)
-                    }
-                } else if (button.id == R.id.actionNext) {
-                    actionViewModel?.let {
-                        viewController.setUpNextButtonInfo(true, it.notVisibleValue)
-                    }
+                val buttonView = viewHolder.getAction(id)
+                val buttonModel = viewModel.actionButtons[index]
+                if (buttonView.id == R.id.actionPrev) {
+                    viewController.setUpPrevButtonInfo(
+                        buttonModel.isEnabled,
+                        buttonModel.notVisibleValue
+                    )
+                } else if (buttonView.id == R.id.actionNext) {
+                    viewController.setUpNextButtonInfo(
+                        buttonModel.isEnabled,
+                        buttonModel.notVisibleValue
+                    )
                 }
-                actionViewModel?.let { action ->
-                    val animHandler = (button.tag ?: AnimationBindHandler()) as AnimationBindHandler
-                    animHandler.tryExecute {
-                        if (animHandler.updateRebindId(action.rebindId)) {
+                val animHandler = (buttonView.tag ?: AnimationBindHandler()) as AnimationBindHandler
+                animHandler.tryExecute {
+                    if (buttonModel.isEnabled) {
+                        if (animHandler.updateRebindId(buttonModel.rebindId)) {
                             animHandler.unregisterAll()
-                            animHandler.tryRegister(action.icon)
-                            animHandler.tryRegister(action.background)
+                            animHandler.tryRegister(buttonModel.icon)
+                            animHandler.tryRegister(buttonModel.background)
                             bindButtonCommon(
-                                button,
+                                buttonView,
                                 viewHolder.multiRippleView,
-                                action,
+                                buttonModel,
                                 viewController,
                                 falsingManager,
                             )
                         }
-                        val visible = action.isVisibleWhenScrubbing || !viewController.isScrubbing
-                        setSemanticButtonVisibleAndAlpha(
-                            viewHolder.getAction(id),
-                            viewController.expandedLayout,
-                            viewController.collapsedLayout,
-                            visible,
-                            action.notVisibleValue,
-                            action.showInCollapsed
-                        )
+                    } else {
+                        animHandler.unregisterAll()
+                        clearButton(buttonView)
                     }
+                    val visible =
+                        buttonModel.isEnabled &&
+                            (buttonModel.isVisibleWhenScrubbing || !viewController.isScrubbing)
+                    setSemanticButtonVisibleAndAlpha(
+                        viewHolder.getAction(id),
+                        viewController.expandedLayout,
+                        viewController.collapsedLayout,
+                        visible,
+                        buttonModel.notVisibleValue,
+                        buttonModel.showInCollapsed
+                    )
                 }
-                    ?: clearButton(button)
             }
         } else {
             // Hide buttons that only appear for semantic actions
@@ -309,22 +315,16 @@
             // Set all generic buttons
             genericButtons.forEachIndexed { index, button ->
                 if (index < viewModel.actionButtons.size) {
-                    viewModel.actionButtons[index]?.let { action ->
-                        bindButtonCommon(
-                            button,
-                            viewHolder.multiRippleView,
-                            action,
-                            viewController,
-                            falsingManager,
-                        )
-                        setVisibleAndAlpha(expandedSet, button.id, visible = true)
-                        setVisibleAndAlpha(
-                            collapsedSet,
-                            button.id,
-                            visible = action.showInCollapsed
-                        )
-                    }
-                        ?: clearButton(button)
+                    val action = viewModel.actionButtons[index]
+                    bindButtonCommon(
+                        button,
+                        viewHolder.multiRippleView,
+                        action,
+                        viewController,
+                        falsingManager,
+                    )
+                    setVisibleAndAlpha(expandedSet, button.id, visible = true)
+                    setVisibleAndAlpha(collapsedSet, button.id, visible = action.showInCollapsed)
                 } else {
                     // Hide any unused buttons
                     clearButton(button)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 0478178..19e3e07 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -46,6 +46,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
@@ -73,6 +74,9 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.qs.PageIndicator
 import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shared.system.SysUiStatsLog
 import com.android.systemui.shared.system.SysUiStatsLog.SMARTSPACE_CARD_REPORTED
 import com.android.systemui.shared.system.SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD
@@ -142,6 +146,7 @@
     private val secureSettings: SecureSettings,
     private val mediaCarouselViewModel: MediaCarouselViewModel,
     private val mediaViewControllerFactory: Provider<MediaViewController>,
+    private val sceneInteractor: SceneInteractor,
 ) : Dumpable {
     /** The current width of the carousel */
     var currentCarouselWidth: Int = 0
@@ -190,9 +195,11 @@
     @VisibleForTesting
     lateinit var settingsButton: View
         private set
+
     private val mediaContent: ViewGroup
     @VisibleForTesting var pageIndicator: PageIndicator
     private var needsReordering: Boolean = false
+    private var isUserInitiatedRemovalQueued: Boolean = false
     private var keysNeedRemoval = mutableSetOf<String>()
     var shouldScrollToKey: Boolean = false
     private var isRtl: Boolean = false
@@ -301,7 +308,11 @@
      * It will be called when the container is out of view.
      */
     lateinit var updateUserVisibility: () -> Unit
-    lateinit var updateHostVisibility: () -> Unit
+    var updateHostVisibility: () -> Unit = {}
+        set(value) {
+            field = value
+            mediaCarouselViewModel.updateHostVisibility = value
+        }
 
     private val isReorderingAllowed: Boolean
         get() = visualStabilityProvider.isReorderingAllowed
@@ -338,6 +349,20 @@
         configurationController.addCallback(configListener)
         if (!mediaFlags.isMediaControlsRefactorEnabled()) {
             setUpListeners()
+        } else {
+            val visualStabilityCallback = OnReorderingAllowedListener {
+                mediaCarouselViewModel.onReorderingAllowed()
+
+                // Update user visibility so that no extra impression will be logged when
+                // activeMediaIndex resets to 0
+                if (this::updateUserVisibility.isInitialized) {
+                    updateUserVisibility()
+                }
+
+                // Let's reset our scroll position
+                mediaCarouselScrollHandler.scrollToStart()
+            }
+            visualStabilityProvider.addPersistentReorderingAllowedListener(visualStabilityCallback)
         }
         mediaFrame.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
             // The pageIndicator is not laid out yet when we get the current state update,
@@ -383,12 +408,15 @@
                 reorderAllPlayers(previousVisiblePlayerKey = null)
             }
 
-            keysNeedRemoval.forEach { removePlayer(it) }
+            keysNeedRemoval.forEach {
+                removePlayer(it, userInitiated = isUserInitiatedRemovalQueued)
+            }
             if (keysNeedRemoval.size > 0) {
                 // Carousel visibility may need to be updated after late removals
                 updateHostVisibility()
             }
             keysNeedRemoval.clear()
+            isUserInitiatedRemovalQueued = false
 
             // Update user visibility so that no extra impression will be logged when
             // activeMediaIndex resets to 0
@@ -472,18 +500,18 @@
 
                     val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
                     if (canRemove && !Utils.useMediaResumption(context)) {
-                        // This view isn't playing, let's remove this! This happens e.g. when
-                        // dismissing/timing out a view. We still have the data around because
-                        // resumption could be on, but we should save the resources and release
-                        // this.
+                        // This media control is both paused and timed out, and the resumption
+                        // setting is off - let's remove it
                         if (isReorderingAllowed) {
-                            onMediaDataRemoved(key)
+                            onMediaDataRemoved(key, userInitiated = MediaPlayerData.isSwipedAway)
                         } else {
+                            isUserInitiatedRemovalQueued = MediaPlayerData.isSwipedAway
                             keysNeedRemoval.add(key)
                         }
                     } else {
                         keysNeedRemoval.remove(key)
                     }
+                    MediaPlayerData.isSwipedAway = false
                 }
 
                 override fun onSmartspaceMediaDataLoaded(
@@ -563,11 +591,12 @@
                             addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
                         }
                     }
+                    MediaPlayerData.isSwipedAway = false
                 }
 
-                override fun onMediaDataRemoved(key: String) {
-                    debugLogger.logMediaRemoved(key)
-                    removePlayer(key)
+                override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
+                    debugLogger.logMediaRemoved(key, userInitiated)
+                    removePlayer(key, userInitiated = userInitiated)
                 }
 
                 override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
@@ -577,9 +606,7 @@
                         if (!immediately) {
                             // Although it wasn't requested, we were able to process the removal
                             // immediately since reordering is allowed. So, notify hosts to update
-                            if (this@MediaCarouselController::updateHostVisibility.isInitialized) {
-                                updateHostVisibility()
-                            }
+                            updateHostVisibility()
                         }
                     } else {
                         keysNeedRemoval.add(key)
@@ -630,9 +657,13 @@
     @VisibleForTesting
     internal fun listenForAnyStateToGoneKeyguardTransition(scope: CoroutineScope): Job {
         return scope.launch {
-            keyguardTransitionInteractor
-                .transition(to = GONE)
-                .filter { it.transitionState == TransitionState.FINISHED }
+            if (SceneContainerFlag.isEnabled) {
+                    sceneInteractor.transitionState.filter { it.isIdle(Scenes.Gone) }
+                } else {
+                    keyguardTransitionInteractor.transition(Edge.create(to = GONE)).filter {
+                        it.transitionState == TransitionState.FINISHED
+                    }
+                }
                 .collect {
                     showMediaCarousel()
                     updateHostVisibility()
@@ -644,7 +675,7 @@
     internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
         return scope.launch {
             keyguardTransitionInteractor
-                .transition(to = LOCKSCREEN)
+                .transition(Edge.create(to = LOCKSCREEN))
                 .filter { it.transitionState == TransitionState.FINISHED }
                 .collect {
                     if (!allowMediaPlayerOnLockScreen) {
@@ -732,12 +763,20 @@
             }
         }
         viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded)
+        controllerByViewModel[commonViewModel] = viewController
         updateViewControllerToState(viewController, noAnimation = true)
         updatePageIndicator()
+        if (
+            commonViewModel is MediaCommonViewModel.MediaControl && commonViewModel.isMediaFromRec
+        ) {
+            mediaCarouselScrollHandler.scrollToPlayer(
+                mediaCarouselScrollHandler.visibleMediaIndex,
+                destIndex = 0
+            )
+        }
         mediaCarouselScrollHandler.onPlayersChanged()
         mediaFrame.requiresRemeasuring = true
         commonViewModel.onAdded(commonViewModel)
-        controllerByViewModel[commonViewModel] = viewController
     }
 
     private fun onUpdated(commonViewModel: MediaCommonViewModel) {
@@ -1023,7 +1062,8 @@
     fun removePlayer(
         key: String,
         dismissMediaData: Boolean = true,
-        dismissRecommendation: Boolean = true
+        dismissRecommendation: Boolean = true,
+        userInitiated: Boolean = false,
     ): MediaControlPanel? {
         if (key == MediaPlayerData.smartspaceMediaKey()) {
             MediaPlayerData.smartspaceMediaData?.let {
@@ -1042,7 +1082,7 @@
 
             if (dismissMediaData) {
                 // Inform the media manager of a potentially late dismissal
-                mediaManager.dismissMediaData(key, delay = 0L)
+                mediaManager.dismissMediaData(key, delay = 0L, userInitiated = userInitiated)
             }
             if (dismissRecommendation) {
                 // Inform the media manager of a potentially late dismissal
@@ -1502,7 +1542,8 @@
         }
     }
 
-    private fun onSwipeToDismiss() {
+    @VisibleForTesting
+    fun onSwipeToDismiss() {
         if (mediaFlags.isMediaControlsRefactorEnabled()) {
             mediaCarouselViewModel.onSwipeToDismiss()
             return
@@ -1521,6 +1562,7 @@
                 it.mIsImpressed = false
             }
         }
+        MediaPlayerData.isSwipedAway = true
         logger.logSwipeDismiss()
         mediaManager.onSwipeToDismiss()
     }
@@ -1547,6 +1589,7 @@
                 "state: ${desiredHostState?.expansion}, " +
                     "only active ${desiredHostState?.showsOnlyActiveMedia}"
             )
+            println("isSwipedAway: ${MediaPlayerData.isSwipedAway}")
         }
     }
 }
@@ -1577,6 +1620,7 @@
     // Whether should prioritize Smartspace card.
     internal var shouldPrioritizeSs: Boolean = false
         private set
+
     internal var smartspaceMediaData: SmartspaceMediaData? = null
         private set
 
@@ -1585,7 +1629,7 @@
         val data: MediaData,
         val key: String,
         val updateTime: Long = 0,
-        val isSsReactivated: Boolean = false
+        val isSsReactivated: Boolean = false,
     )
 
     private val comparator =
@@ -1610,6 +1654,9 @@
     // A map that tracks order of visible media players before they get reordered.
     private val visibleMediaPlayers = LinkedHashMap<String, MediaSortKey>()
 
+    // Whether the user swiped away the carousel since its last update
+    internal var isSwipedAway: Boolean = false
+
     fun addMediaPlayer(
         key: String,
         data: MediaData,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
index ebf1c6a..1be25a7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
@@ -53,8 +53,16 @@
             { "add player $str1, active: $bool1" }
         )
 
-    fun logMediaRemoved(key: String) =
-        buffer.log(TAG, LogLevel.DEBUG, { str1 = key }, { "removing player $str1" })
+    fun logMediaRemoved(key: String, userInitiated: Boolean) =
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = key
+                bool1 = userInitiated
+            },
+            { "removing player $str1, by user $bool1" }
+        )
 
     fun logRecommendationLoaded(key: String, isActive: Boolean) =
         buffer.log(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index e6c785e..0bc3c439 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -786,10 +786,11 @@
             if (mKey != null) {
                 closeGuts();
                 if (!mMediaDataManagerLazy.get().dismissMediaData(mKey,
-                        MediaViewController.GUTS_ANIMATION_DURATION + 100)) {
+                        /* delay */ MediaViewController.GUTS_ANIMATION_DURATION + 100,
+                        /* userInitiated */ true)) {
                     Log.w(TAG, "Manager failed to dismiss media " + mKey);
                     // Remove directly from carousel so user isn't stuck with defunct controls
-                    mMediaCarouselController.removePlayer(mKey, false, false);
+                    mMediaCarouselController.removePlayer(mKey, false, false, true);
                 }
             } else {
                 Log.w(TAG, "Dismiss media with null notification. Token uid="
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 2b59858..3837708 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -709,12 +709,6 @@
 
         // For Turbulence noise.
         val loadingEffectView = mediaViewHolder.loadingEffectView
-        turbulenceNoiseAnimationConfig =
-            createTurbulenceNoiseConfig(
-                loadingEffectView,
-                turbulenceNoiseView,
-                colorSchemeTransition
-            )
         noiseDrawCallback =
             object : PaintDrawCallback {
                 override fun onDraw(paint: Paint) {
@@ -809,6 +803,14 @@
 
     fun setUpTurbulenceNoise() {
         if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        if (!this::turbulenceNoiseAnimationConfig.isInitialized) {
+            turbulenceNoiseAnimationConfig =
+                createTurbulenceNoiseConfig(
+                    mediaViewHolder.loadingEffectView,
+                    mediaViewHolder.turbulenceNoiseView,
+                    colorSchemeTransition
+                )
+        }
         if (Flags.shaderlibLoadingEffectRefactor()) {
             if (!this::loadingEffect.isInitialized) {
                 loadingEffect =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index eca76b6..91050c8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -105,7 +105,7 @@
                 updateViewVisibility()
             }
 
-            override fun onMediaDataRemoved(key: String) {
+            override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
                 updateViewVisibility()
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
index 96a8239..4e90936 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
@@ -26,21 +26,15 @@
 import com.android.systemui.media.controls.shared.model.MediaCommonModel
 import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaUiEventLogger
-import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
 import com.android.systemui.util.Utils
-import com.android.systemui.util.kotlin.pairwiseBy
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /** Models UI state and handles user inputs for media carousel */
@@ -60,46 +54,32 @@
     private val mediaFlags: MediaFlags,
 ) {
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     val mediaItems: StateFlow<List<MediaCommonViewModel>> =
-        conflatedCallbackFlow {
-                val listener = OnReorderingAllowedListener { trySend(Unit) }
-                visualStabilityProvider.addPersistentReorderingAllowedListener(listener)
-                trySend(Unit)
-                awaitClose { visualStabilityProvider.removeReorderingAllowedListener(listener) }
-            }
-            .flatMapLatest {
-                combine(interactor.isMediaFromRec, interactor.sortedMedia) {
-                    isRecsToMedia,
-                    sortedItems ->
-                    buildList {
-                        shouldReorder = isRecsToMedia
-                        val reorderAllowed = isReorderingAllowed()
-                        sortedItems.forEach { commonModel ->
-                            if (!reorderAllowed || !modelsPendingRemoval.contains(commonModel)) {
-                                when (commonModel) {
-                                    is MediaCommonModel.MediaControl ->
-                                        add(toViewModel(commonModel))
-                                    is MediaCommonModel.MediaRecommendations ->
-                                        add(toViewModel(commonModel))
-                                }
+        interactor.currentMedia
+            .map { sortedItems ->
+                val mediaList = buildList {
+                    sortedItems.forEach { commonModel ->
+                        // When view is started we should make sure to clean models that are pending
+                        // removal.
+                        // This action should only be triggered once.
+                        if (!allowReorder || !modelsPendingRemoval.contains(commonModel)) {
+                            when (commonModel) {
+                                is MediaCommonModel.MediaControl -> add(toViewModel(commonModel))
+                                is MediaCommonModel.MediaRecommendations ->
+                                    add(toViewModel(commonModel))
                             }
                         }
-                        if (reorderAllowed) {
-                            modelsPendingRemoval.clear()
-                        }
                     }
                 }
-            }
-            .pairwiseBy { old, new ->
-                // This condition can only happen when view is attached. So the old emit is of the
-                // most recent list updated.
-                // If the old list is empty, it is okay to emit the new ordered list.
-                if (isReorderingAllowed() || shouldReorder || old.isEmpty()) {
-                    new
-                } else {
-                    old
+                if (allowReorder) {
+                    if (modelsPendingRemoval.size > 0) {
+                        updateHostVisibility()
+                    }
+                    modelsPendingRemoval.clear()
                 }
+                allowReorder = false
+
+                mediaList
             }
             .stateIn(
                 scope = applicationScope,
@@ -107,6 +87,8 @@
                 initialValue = emptyList(),
             )
 
+    var updateHostVisibility: () -> Unit = {}
+
     private val mediaControlByInstanceId =
         mutableMapOf<InstanceId, MediaCommonViewModel.MediaControl>()
 
@@ -114,13 +96,18 @@
 
     private var modelsPendingRemoval: MutableSet<MediaCommonModel> = mutableSetOf()
 
-    private var shouldReorder = true
+    private var allowReorder = false
 
     fun onSwipeToDismiss() {
         logger.logSwipeDismiss()
         interactor.onSwipeToDismiss()
     }
 
+    fun onReorderingAllowed() {
+        allowReorder = true
+        interactor.reorderMedia()
+    }
+
     private fun toViewModel(
         commonModel: MediaCommonModel.MediaControl
     ): MediaCommonViewModel.MediaControl {
@@ -138,6 +125,7 @@
                         mediaControlByInstanceId.remove(instanceId)
                     },
                     onUpdated = { onMediaControlAddedOrUpdated(it, commonModel) },
+                    isMediaFromRec = commonModel.isMediaFromRec
                 )
                 .also { mediaControlByInstanceId[instanceId] = it }
     }
@@ -213,7 +201,11 @@
     ) {
         if (immediatelyRemove || isReorderingAllowed()) {
             interactor.dismissSmartspaceRecommendation(commonModel.recsLoadingModel.key, 0L)
-            // TODO if not immediate remove update host visibility
+            if (!immediatelyRemove) {
+                // Although it wasn't requested, we were able to process the removal
+                // immediately since reordering is allowed. So, notify hosts to update
+                updateHostVisibility()
+            }
         } else {
             modelsPendingRemoval.add(commonModel)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
index aeaa82e..a96d75c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
@@ -32,6 +32,7 @@
         override val onAdded: (MediaCommonViewModel) -> Unit,
         override val onRemoved: (Boolean) -> Unit,
         override val onUpdated: (MediaCommonViewModel) -> Unit,
+        val isMediaFromRec: Boolean = false,
     ) : MediaCommonViewModel()
 
     data class MediaRecommendations(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index bc364c3..1944f07 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -20,6 +20,7 @@
 import android.content.pm.PackageManager
 import android.media.session.MediaController
 import android.media.session.MediaSession.Token
+import android.media.session.PlaybackState
 import android.text.TextUtils
 import android.util.Log
 import androidx.constraintlayout.widget.ConstraintSet
@@ -40,16 +41,14 @@
 import com.android.systemui.monet.ColorScheme
 import com.android.systemui.monet.Style
 import com.android.systemui.res.R
-import com.android.systemui.util.kotlin.sample
 import java.util.concurrent.Executor
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
 
 /** Models UI state and handles user input for a media control. */
 class MediaControlViewModel(
@@ -60,31 +59,20 @@
     private val logger: MediaUiEventLogger,
 ) {
 
-    private val isAnyButtonClicked: MutableStateFlow<Boolean> = MutableStateFlow(false)
-
-    private val playTurbulenceNoise: Flow<Boolean> =
-        interactor.mediaControl.sample(
-            combine(isAnyButtonClicked, interactor.isStartedPlaying) {
-                    isButtonClicked,
-                    isStartedPlaying ->
-                    isButtonClicked && isStartedPlaying
-                }
-                .distinctUntilChanged()
-        )
-
     @OptIn(ExperimentalCoroutinesApi::class)
     val player: Flow<MediaPlayerViewModel?> =
         interactor.onAnyMediaConfigurationChange
             .flatMapLatest {
-                combine(playTurbulenceNoise, interactor.mediaControl) {
-                    playTurbulenceNoise,
-                    mediaControl ->
-                    mediaControl?.let { toViewModel(it, playTurbulenceNoise) }
+                interactor.mediaControl.map { mediaControl ->
+                    mediaControl?.let { toViewModel(it) }
                 }
             }
             .distinctUntilChanged()
             .flowOn(backgroundDispatcher)
 
+    private var isPlaying = false
+    private var isAnyButtonClicked = false
+
     private fun onDismissMediaData(
         token: Token?,
         uid: Int,
@@ -95,10 +83,8 @@
         interactor.removeMediaControl(token, instanceId, MEDIA_PLAYER_ANIMATION_DELAY)
     }
 
-    private suspend fun toViewModel(
-        model: MediaControlModel,
-        playTurbulenceNoise: Boolean
-    ): MediaPlayerViewModel? {
+    private suspend fun toViewModel(model: MediaControlModel): MediaPlayerViewModel? {
+        val mediaController = model.token?.let { MediaController(applicationContext, it) }
         val wallpaperColors =
             MediaArtworkHelper.getWallpaperColor(
                 applicationContext,
@@ -118,8 +104,14 @@
 
         val gutsViewModel = toGutsViewModel(model, scheme)
 
+        // Set playing state
+        val wasPlaying = isPlaying
+        isPlaying =
+            mediaController?.playbackState?.let { it.state == PlaybackState.STATE_PLAYING } ?: false
+
         // Resetting button clicks state.
-        isAnyButtonClicked.value = false
+        val wasButtonClicked = isAnyButtonClicked
+        isAnyButtonClicked = false
 
         return MediaPlayerViewModel(
             contentDescription = { gutsVisible ->
@@ -144,7 +136,7 @@
             shouldAddGradient = wallpaperColors != null,
             colorScheme = scheme,
             canShowTime = canShowScrubbingTimeViews(model.semanticActionButtons),
-            playTurbulenceNoise = playTurbulenceNoise,
+            playTurbulenceNoise = isPlaying && !wasPlaying && wasButtonClicked,
             useSemanticActions = model.semanticActionButtons != null,
             actionButtons = toActionViewModels(model),
             outputSwitcher = toOutputSwitcherViewModel(model),
@@ -168,9 +160,7 @@
                     seekBarViewModel.updateStaticProgress(model.resumeProgress)
                 } else {
                     backgroundExecutor.execute {
-                        seekBarViewModel.updateController(
-                            model.token?.let { MediaController(applicationContext, it) }
-                        )
+                        seekBarViewModel.updateController(mediaController)
                     }
                 }
             }
@@ -283,16 +273,17 @@
         )
     }
 
-    private fun toActionViewModels(model: MediaControlModel): List<MediaActionViewModel?> {
+    private fun toActionViewModels(model: MediaControlModel): List<MediaActionViewModel> {
         val semanticActionButtons =
             model.semanticActionButtons?.let { mediaButton ->
-                with(mediaButton) {
-                    val isScrubbingTimeEnabled = canShowScrubbingTimeViews(mediaButton)
-                    SEMANTIC_ACTIONS_ALL.map { buttonId ->
-                        getActionById(buttonId)?.let {
-                            toSemanticActionViewModel(model, it, buttonId, isScrubbingTimeEnabled)
-                        }
-                    }
+                val isScrubbingTimeEnabled = canShowScrubbingTimeViews(mediaButton)
+                SEMANTIC_ACTIONS_ALL.map { buttonId ->
+                    toSemanticActionViewModel(
+                        model,
+                        mediaButton.getActionById(buttonId),
+                        buttonId,
+                        isScrubbingTimeEnabled
+                    )
                 }
             }
         val notifActionButtons =
@@ -304,7 +295,7 @@
 
     private fun toSemanticActionViewModel(
         model: MediaControlModel,
-        mediaAction: MediaAction,
+        mediaAction: MediaAction?,
         buttonId: Int,
         canShowScrubbingTimeViews: Boolean
     ): MediaActionViewModel {
@@ -312,9 +303,9 @@
         val hideWhenScrubbing = SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.contains(buttonId)
         val shouldHideWhenScrubbing = canShowScrubbingTimeViews && hideWhenScrubbing
         return MediaActionViewModel(
-            icon = mediaAction.icon,
-            contentDescription = mediaAction.contentDescription,
-            background = mediaAction.background,
+            icon = mediaAction?.icon,
+            contentDescription = mediaAction?.contentDescription,
+            background = mediaAction?.background,
             isVisibleWhenScrubbing = !shouldHideWhenScrubbing,
             notVisibleValue =
                 if (
@@ -326,11 +317,11 @@
                     ConstraintSet.GONE
                 },
             showInCollapsed = showInCollapsed,
-            rebindId = mediaAction.rebindId,
+            rebindId = mediaAction?.rebindId,
             buttonId = buttonId,
-            isEnabled = mediaAction.action != null,
+            isEnabled = mediaAction?.action != null,
             onClicked = { id ->
-                mediaAction.action?.let {
+                mediaAction?.action?.let {
                     onButtonClicked(id, model.uid, model.packageName, model.instanceId, it)
                 }
             },
@@ -366,7 +357,7 @@
     ) {
         logger.logTapAction(id, uid, packageName, instanceId)
         // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
-        isAnyButtonClicked.value = true
+        isAnyButtonClicked = true
         action.run()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
index d1014e8..4334341 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
@@ -35,7 +35,7 @@
     val canShowTime: Boolean,
     val playTurbulenceNoise: Boolean,
     val useSemanticActions: Boolean,
-    val actionButtons: List<MediaActionViewModel?>,
+    val actionButtons: List<MediaActionViewModel>,
     val outputSwitcher: MediaOutputSwitcherViewModel,
     val gutsMenu: GutsViewModel,
     val onClicked: (Expandable) -> Unit,
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index 88a5f78..061e7ec 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -48,7 +48,7 @@
         }
 
         @Override
-        public void onMediaDataRemoved(@NonNull String key) {
+        public void onMediaDataRemoved(@NonNull String key, boolean userInitiated) {
             final boolean hasActiveMedia = mMediaDataManager.hasActiveMedia();
             if (DEBUG) {
                 Log.d(TAG, "onMediaDataRemoved(" + key + "), mAdded=" + mAdded + ", hasActiveMedia="
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index 412c006..9265bfb 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -140,10 +140,11 @@
 
         val bitmapShader = bitmapShader ?: return
         val thumbnailData = thumbnailData ?: return
+        val thumbnail = thumbnailData.thumbnail ?: return
         val display = context.display ?: return
         val windowMetrics = windowManager.maximumWindowMetrics
 
-        previewRect.set(0, 0, thumbnailData.thumbnail.width, thumbnailData.thumbnail.height)
+        previewRect.set(0, 0, thumbnail.width, thumbnail.height)
 
         val currentRotation: Int = display.rotation
         val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
index 89e4760..a144dc2 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags
 import dagger.Lazy
 import javax.inject.Inject
 
@@ -48,7 +49,7 @@
      * Returns an override value for the given [flag] or `null` if the scene framework isn't enabled
      * or if the flag value doesn't need to be overridden.
      */
-    fun flagValueOverride(flag: Int): Boolean? {
+    fun flagValueOverride(@SystemUiStateFlags flag: Long): Boolean? {
         if (!SceneContainerFlag.isEnabled) {
             return null
         }
@@ -79,7 +80,7 @@
          * to be overridden by the scene framework.
          */
         val EvaluatorByFlag =
-            mapOf<Int, (SceneContainerPluginState) -> Boolean>(
+            mapOf<Long, (SceneContainerPluginState) -> Boolean>(
                 SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it.scene != Scenes.Gone },
                 SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to
                     {
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 2dd2327..67fe0e9 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -23,6 +23,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 
 import dalvik.annotation.optimization.NeverCompile;
 
@@ -42,10 +43,10 @@
 
     private final DisplayTracker mDisplayTracker;
     private final SceneContainerPlugin mSceneContainerPlugin;
-    private @QuickStepContract.SystemUiStateFlags int mFlags;
+    private @SystemUiStateFlags long mFlags;
     private final List<SysUiStateCallback> mCallbacks = new ArrayList<>();
-    private int mFlagsToSet = 0;
-    private int mFlagsToClear = 0;
+    private long mFlagsToSet = 0;
+    private long mFlagsToClear = 0;
 
     public SysUiState(DisplayTracker displayTracker, SceneContainerPlugin sceneContainerPlugin) {
         mDisplayTracker = displayTracker;
@@ -67,12 +68,17 @@
     }
 
     /** Returns the current sysui state flags. */
-    public int getFlags() {
+    @SystemUiStateFlags
+    public long getFlags() {
         return mFlags;
     }
 
+    public boolean isFlagEnabled(@SystemUiStateFlags long flag) {
+        return (mFlags & flag) != 0;
+    }
+
     /** Methods to this call can be chained together before calling {@link #commitUpdate(int)}. */
-    public SysUiState setFlag(int flag, boolean enabled) {
+    public SysUiState setFlag(@SystemUiStateFlags long flag, boolean enabled) {
         final Boolean overrideOrNull = mSceneContainerPlugin.flagValueOverride(flag);
         if (overrideOrNull != null && enabled != overrideOrNull) {
             if (DEBUG) {
@@ -91,7 +97,7 @@
         return this;
     }
 
-    /** Call to save all the flags updated from {@link #setFlag(int, boolean)}. */
+    /** Call to save all the flags updated from {@link #setFlag(long, boolean)}. */
     public void commitUpdate(int displayId) {
         updateFlags(displayId);
         mFlagsToSet = 0;
@@ -105,14 +111,14 @@
             return;
         }
 
-        int newState = mFlags;
+        long newState = mFlags;
         newState |= mFlagsToSet;
         newState &= ~mFlagsToClear;
         notifyAndSetSystemUiStateChanged(newState, mFlags);
     }
 
     /** Notify all those who are registered that the state has changed. */
-    private void notifyAndSetSystemUiStateChanged(int newFlags, int oldFlags) {
+    private void notifyAndSetSystemUiStateChanged(long newFlags, long oldFlags) {
         if (DEBUG) {
             Log.d(TAG, "SysUiState changed: old=" + oldFlags + " new=" + newFlags);
         }
@@ -137,7 +143,7 @@
     /** Callback to be notified whenever system UI state flags are changed. */
     public interface SysUiStateCallback{
         /** To be called when any SysUiStateFlag gets updated */
-        void onSystemUiStateChanged(@QuickStepContract.SystemUiStateFlags int sysUiFlags);
+        void onSystemUiStateChanged(@SystemUiStateFlags long sysUiFlags);
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
index 5c49156..1e18f24 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
@@ -40,7 +40,7 @@
  */
 fun SysUiState.updateFlags(
     @DisplayId displayId: Int,
-    vararg flagValuePairs: Pair<Int, Boolean>,
+    vararg flagValuePairs: Pair<Long, Boolean>,
 ) {
     flagValuePairs.forEach { (flag, enabled) -> setFlag(flag, enabled) }
     commitUpdate(displayId)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index a6b6d61..80c4379 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -128,7 +128,7 @@
     private boolean mLongPressHomeEnabled;
     private boolean mAssistantTouchGestureEnabled;
     private int mNavBarMode;
-    private int mA11yButtonState;
+    private long mA11yButtonState;
     private int mRotationWatcherRotation;
     private boolean mTogglingNavbarTaskbar;
     private boolean mWallpaperVisible;
@@ -374,7 +374,7 @@
      * {@link Secure#ACCESSIBILITY_BUTTON_MODE_GESTURE}, otherwise it is reset to 0.
      */
     private void updateA11yState() {
-        final int prevState = mA11yButtonState;
+        final long prevState = mA11yButtonState;
         final boolean clickable;
         final boolean longClickable;
         if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
@@ -431,7 +431,7 @@
      * 48 = the combination of {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
      * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
      */
-    public int getA11yButtonState() {
+    public long getA11yButtonState() {
         return mA11yButtonState;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 906ebad..0e819c2 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -1602,7 +1602,7 @@
     void updateAccessibilityStateFlags() {
         mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
         if (mView != null) {
-            int a11yFlags = mNavBarHelper.getA11yButtonState();
+            long a11yFlags = mNavBarHelper.getA11yButtonState();
             boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
             boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
             mView.setAccessibilityButtonState(clickable, longClickable);
@@ -1611,7 +1611,7 @@
     }
 
     public void updateSystemUiStateFlags() {
-        int a11yFlags = mNavBarHelper.getA11yButtonState();
+        long a11yFlags = mNavBarHelper.getA11yButtonState();
         boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
         boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index f67973b..b360af0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -298,7 +298,7 @@
     }
 
     private void updateSysuiFlags() {
-        int a11yFlags = mNavBarHelper.getA11yButtonState();
+        long a11yFlags = mNavBarHelper.getA11yButtonState();
         boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
         boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 295b293..9487085 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -84,6 +84,7 @@
 import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -270,7 +271,8 @@
     private BackAnimation mBackAnimation;
     private int mLeftInset;
     private int mRightInset;
-    private int mSysUiFlags;
+    @SystemUiStateFlags
+    private long mSysUiFlags;
 
     // For Tf-Lite model.
     private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
@@ -334,7 +336,7 @@
     private final SysUiState.SysUiStateCallback mSysUiStateCallback =
             new SysUiState.SysUiStateCallback() {
         @Override
-        public void onSystemUiStateChanged(int sysUiFlags) {
+        public void onSystemUiStateChanged(@SystemUiStateFlags long sysUiFlags) {
             mSysUiFlags = sysUiFlags;
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
index 3907a72..5e6ee4d 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
@@ -16,12 +16,20 @@
 
 package com.android.systemui.qrcodescanner.dagger
 
+import com.android.systemui.Flags
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.QRCodeScannerTile
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor
+import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.qs.tiles.impl.qr.ui.QRCodeScannerTileMapper
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
 import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
@@ -54,5 +62,24 @@
                     ),
                 instanceId = uiEventLogger.getNewInstanceId(),
             )
+
+        /** Inject QR Code Scanner Tile into tileViewModelMap in QSModule. */
+        @Provides
+        @IntoMap
+        @StringKey(QR_CODE_SCANNER_TILE_SPEC)
+        fun provideQRCodeScannerTileViewModel(
+            factory: QSTileViewModelFactory.Static<QRCodeScannerTileModel>,
+            mapper: QRCodeScannerTileMapper,
+            stateInteractor: QRCodeScannerTileDataInteractor,
+            userActionInteractor: QRCodeScannerTileUserActionInteractor
+        ): QSTileViewModel =
+            if (Flags.qsNewTilesFuture())
+                factory.create(
+                    TileSpec.create(QR_CODE_SCANNER_TILE_SPEC),
+                    userActionInteractor,
+                    stateInteractor,
+                    mapper,
+                )
+            else StubQSTileViewModel
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt b/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt
index 2c8a5a4..1336d64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt
@@ -18,6 +18,7 @@
 
 import android.service.quicksettings.Tile
 import android.text.TextUtils
+import android.widget.Switch
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.external.CustomTile
 import com.android.systemui.qs.nano.QsTileState
@@ -44,8 +45,8 @@
         }
     label?.let { state.label = it.toString() }
     secondaryLabel?.let { state.secondaryLabel = it.toString() }
-    if (this is QSTile.BooleanState) {
-        state.booleanState = value
+    if (expandedAccessibilityClassName == Switch::class.java.name) {
+        state.booleanState = state.state == QsTileState.ACTIVE
     }
     return state
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index d26ae0a..5d35a69 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -42,6 +42,7 @@
 import android.util.Log;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
+import android.widget.Button;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
@@ -502,6 +503,8 @@
         if (state instanceof BooleanState) {
             state.expandedAccessibilityClassName = Switch.class.getName();
             ((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE);
+        } else {
+            state.expandedAccessibilityClassName = Button.class.getName();
         }
 
     }
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 2a726c2..24b7a01 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -19,6 +19,8 @@
 import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
 import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PENDING_INTENT;
 
+import static com.android.systemui.Flags.qsCustomTileClickGuaranteedBugFix;
+
 import android.app.ActivityManager;
 import android.app.compat.CompatChanges;
 import android.content.BroadcastReceiver;
@@ -88,6 +90,7 @@
     private static final int MSG_ON_REMOVED = 1;
     private static final int MSG_ON_CLICK = 2;
     private static final int MSG_ON_UNLOCK_COMPLETE = 3;
+    private static final int MSG_ON_STOP_LISTENING = 4;
 
     // Bind retry control.
     private static final int MAX_BIND_RETRIES = 5;
@@ -368,6 +371,16 @@
                 onUnlockComplete();
             }
         }
+        if (qsCustomTileClickGuaranteedBugFix()) {
+            if (queue.contains(MSG_ON_STOP_LISTENING)) {
+                if (mDebug) Log.d(TAG, "Handling pending onStopListening " + getComponent());
+                if (mListening) {
+                    onStopListening();
+                } else {
+                    Log.w(TAG, "Trying to stop listening when not listening " + getComponent());
+                }
+            }
+        }
         if (queue.contains(MSG_ON_REMOVED)) {
             if (mDebug) Log.d(TAG, "Handling pending onRemoved " + getComponent());
             if (mListening) {
@@ -586,10 +599,15 @@
 
     @Override
     public void onStopListening() {
-        if (mDebug) Log.d(TAG, "onStopListening " + getComponent());
-        mListening = false;
-        if (isNotNullAndFailedAction(mOptionalWrapper, QSTileServiceWrapper::onStopListening)) {
-            handleDeath();
+        if (qsCustomTileClickGuaranteedBugFix() && hasPendingClick()) {
+            Log.d(TAG, "Enqueue stop listening");
+            queueMessage(MSG_ON_STOP_LISTENING);
+        } else {
+            if (mDebug) Log.d(TAG, "onStopListening " + getComponent());
+            mListening = false;
+            if (isNotNullAndFailedAction(mOptionalWrapper, QSTileServiceWrapper::onStopListening)) {
+                handleDeath();
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index f8bf0a6..6bc5095 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.qs.external;
 
+import static com.android.systemui.Flags.qsCustomTileClickGuaranteedBugFix;
+
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -37,6 +39,7 @@
 
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Manages the priority which lets {@link TileServices} make decisions about which tiles
@@ -72,6 +75,8 @@
     private boolean mPendingBind = true;
     private boolean mStarted = false;
 
+    private final AtomicBoolean mListeningFromRequest = new AtomicBoolean(false);
+
     TileServiceManager(TileServices tileServices, Handler handler, ComponentName component,
             UserTracker userTracker, TileLifecycleManager.Factory tileLifecycleManagerFactory,
             CustomTileAddedRepository customTileAddedRepository) {
@@ -159,15 +164,30 @@
         }
     }
 
+    void onStartListeningFromRequest() {
+        mListeningFromRequest.set(true);
+        mStateManager.onStartListening();
+    }
+
     public void setLastUpdate(long lastUpdate) {
         mLastUpdate = lastUpdate;
         if (mBound && isActiveTile()) {
-            mStateManager.onStopListening();
-            setBindRequested(false);
+            if (qsCustomTileClickGuaranteedBugFix()) {
+                if (mListeningFromRequest.compareAndSet(true, false)) {
+                    stopListeningAndUnbind();
+                }
+            } else {
+                stopListeningAndUnbind();
+            }
         }
         mServices.recalculateBindAllowance();
     }
 
+    private void stopListeningAndUnbind() {
+        mStateManager.onStopListening();
+        setBindRequested(false);
+    }
+
     public void handleDestroy() {
         setBindAllowed(false);
         mServices.getContext().unregisterReceiver(mUninstallReceiver);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 8278c79..d457e88 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.qs.external;
 
+import static com.android.systemui.Flags.qsCustomTileClickGuaranteedBugFix;
+
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -222,9 +224,13 @@
                 return;
             }
             service.setBindRequested(true);
-            try {
-                service.getTileService().onStartListening();
-            } catch (RemoteException e) {
+            if (qsCustomTileClickGuaranteedBugFix()) {
+                service.onStartListeningFromRequest();
+            } else {
+                try {
+                    service.getTileService().onStartListening();
+                } catch (RemoteException e) {
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index 0696fbe..2cc3985 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -29,8 +29,10 @@
 import com.android.systemui.qs.panels.shared.model.GridConsistencyLog
 import com.android.systemui.qs.panels.shared.model.GridLayoutType
 import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
+import com.android.systemui.qs.panels.shared.model.StretchedGridLayoutType
 import com.android.systemui.qs.panels.ui.compose.GridLayout
 import com.android.systemui.qs.panels.ui.compose.InfiniteGridLayout
+import com.android.systemui.qs.panels.ui.compose.StretchedGridLayout
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -63,6 +65,14 @@
         }
 
         @Provides
+        @IntoSet
+        fun provideStretchedGridLayout(
+            gridLayout: StretchedGridLayout
+        ): Pair<GridLayoutType, GridLayout> {
+            return Pair(StretchedGridLayoutType, gridLayout)
+        }
+
+        @Provides
         fun provideGridLayoutMap(
             entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridLayout>>
         ): Map<GridLayoutType, GridLayout> {
@@ -70,6 +80,13 @@
         }
 
         @Provides
+        fun provideGridLayoutTypes(
+            entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridLayout>>
+        ): Set<GridLayoutType> {
+            return entries.map { it.first }.toSet()
+        }
+
+        @Provides
         @IntoSet
         fun provideGridConsistencyInteractor(
             consistencyInteractor: InfiniteGridConsistencyInteractor
@@ -78,6 +95,14 @@
         }
 
         @Provides
+        @IntoSet
+        fun provideStretchedGridConsistencyInteractor(
+            consistencyInteractor: NoopGridConsistencyInteractor
+        ): Pair<GridLayoutType, GridTypeConsistencyInteractor> {
+            return Pair(StretchedGridLayoutType, consistencyInteractor)
+        }
+
+        @Provides
         fun provideGridConsistencyInteractorMap(
             entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridTypeConsistencyInteractor>>
         ): Map<GridLayoutType, GridTypeConsistencyInteractor> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
index 542d0cb..31795d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
@@ -26,10 +26,17 @@
 
 interface GridLayoutTypeRepository {
     val layout: StateFlow<GridLayoutType>
+    fun setLayout(type: GridLayoutType)
 }
 
 @SysUISingleton
 class GridLayoutTypeRepositoryImpl @Inject constructor() : GridLayoutTypeRepository {
     private val _layout: MutableStateFlow<GridLayoutType> = MutableStateFlow(InfiniteGridLayoutType)
     override val layout = _layout.asStateFlow()
+
+    override fun setLayout(type: GridLayoutType) {
+        if (_layout.value != type) {
+            _layout.value = type
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
index b6be578..4af1b22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
@@ -20,9 +20,13 @@
 import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
 import com.android.systemui.qs.panels.shared.model.GridLayoutType
 import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
 
 @SysUISingleton
-class GridLayoutTypeInteractor @Inject constructor(repo: GridLayoutTypeRepository) {
-    val layout: Flow<GridLayoutType> = repo.layout
+class GridLayoutTypeInteractor @Inject constructor(private val repo: GridLayoutTypeRepository) {
+    val layout: StateFlow<GridLayoutType> = repo.layout
+
+    fun setLayoutType(type: GridLayoutType) {
+        repo.setLayout(type)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
index 74e906c..b437f64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
@@ -18,6 +18,8 @@
 
 import android.util.Log
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.shared.model.TileRow
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import javax.inject.Inject
 
@@ -35,7 +37,7 @@
      */
     override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> {
         val newTiles: MutableList<TileSpec> = mutableListOf()
-        val row = TileRow(columns = gridSizeInteractor.columns.value)
+        val row = TileRow<TileSpec>(columns = gridSizeInteractor.columns.value)
         val iconTilesSet = iconTilesInteractor.iconTilesSpecs.value
         val tilesQueue =
             ArrayDeque(
@@ -54,7 +56,7 @@
 
         while (tilesQueue.isNotEmpty()) {
             if (row.isFull()) {
-                newTiles.addAll(row.tileSpecs())
+                newTiles.addAll(row.tiles.map { it.tile })
                 row.clear()
             }
 
@@ -66,13 +68,13 @@
                 // We'll try to either add an icon tile from the queue to complete the row, or
                 // remove an icon tile from the current row to free up space.
 
-                val iconTile: SizedTile? = tilesQueue.firstOrNull { it.width == 1 }
+                val iconTile: SizedTile<TileSpec>? = tilesQueue.firstOrNull { it.width == 1 }
                 if (iconTile != null) {
                     tilesQueue.remove(iconTile)
                     tilesQueue.addFirst(tile)
                     row.maybeAddTile(iconTile)
                 } else {
-                    val tileToRemove: SizedTile? = row.findLastIconTile()
+                    val tileToRemove: SizedTile<TileSpec>? = row.findLastIconTile()
                     if (tileToRemove != null) {
                         row.removeTile(tileToRemove)
                         row.maybeAddTile(tile)
@@ -84,7 +86,7 @@
                         // If the row does not have an icon tile, add the incomplete row.
                         // Note: this shouldn't happen because an icon tile is guaranteed to be in a
                         // row that doesn't have enough space for a large tile.
-                        val tileSpecs = row.tileSpecs()
+                        val tileSpecs = row.tiles.map { it.tile }
                         Log.wtf(TAG, "Uneven row does not have an icon tile to remove: $tileSpecs")
                         newTiles.addAll(tileSpecs)
                         row.clear()
@@ -95,48 +97,11 @@
         }
 
         // Add last row that might be incomplete
-        newTiles.addAll(row.tileSpecs())
+        newTiles.addAll(row.tiles.map { it.tile })
 
         return newTiles.toList()
     }
 
-    /** Tile with a width representing the number of columns it should take. */
-    private data class SizedTile(val spec: TileSpec, val width: Int)
-
-    private class TileRow(private val columns: Int) {
-        private var availableColumns = columns
-        private val tiles: MutableList<SizedTile> = mutableListOf()
-
-        fun tileSpecs(): List<TileSpec> {
-            return tiles.map { it.spec }
-        }
-
-        fun maybeAddTile(tile: SizedTile): Boolean {
-            if (availableColumns - tile.width >= 0) {
-                tiles.add(tile)
-                availableColumns -= tile.width
-                return true
-            }
-            return false
-        }
-
-        fun findLastIconTile(): SizedTile? {
-            return tiles.findLast { it.width == 1 }
-        }
-
-        fun removeTile(tile: SizedTile) {
-            tiles.remove(tile)
-            availableColumns += tile.width
-        }
-
-        fun clear() {
-            tiles.clear()
-            availableColumns = columns
-        }
-
-        fun isFull(): Boolean = availableColumns == 0
-    }
-
     private companion object {
         const val TAG = "InfiniteGridConsistencyInteractor"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt
new file mode 100644
index 0000000..97ceacc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import javax.inject.Inject
+
+@SysUISingleton
+class NoopConsistencyInteractor @Inject constructor() : GridTypeConsistencyInteractor {
+    override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> = tiles
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
index 23110dc..501730a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
@@ -25,3 +25,9 @@
 
 /** Grid type representing a scrollable vertical grid. */
 data object InfiniteGridLayoutType : GridLayoutType
+
+/**
+ * Grid type representing a scrollable vertical grid where tiles will stretch to fill in empty
+ * spaces.
+ */
+data object StretchedGridLayoutType : GridLayoutType
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/TileRow.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/TileRow.kt
new file mode 100644
index 0000000..7e4381b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/TileRow.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.shared.model
+
+/** Represents a tile of type [T] associated with a width */
+data class SizedTile<T>(val tile: T, val width: Int)
+
+/** Represents a row of [SizedTile] with a maximum width of [columns] */
+class TileRow<T>(private val columns: Int) {
+    private var availableColumns = columns
+    private val _tiles: MutableList<SizedTile<T>> = mutableListOf()
+    val tiles: List<SizedTile<T>>
+        get() = _tiles.toList()
+
+    fun maybeAddTile(tile: SizedTile<T>): Boolean {
+        if (availableColumns - tile.width >= 0) {
+            _tiles.add(tile)
+            availableColumns -= tile.width
+            return true
+        }
+        return false
+    }
+
+    fun findLastIconTile(): SizedTile<T>? {
+        return _tiles.findLast { it.width == 1 }
+    }
+
+    fun removeTile(tile: SizedTile<T>) {
+        _tiles.remove(tile)
+        availableColumns += tile.width
+    }
+
+    fun clear() {
+        _tiles.clear()
+        availableColumns = columns
+    }
+
+    fun isFull(): Boolean = availableColumns == 0
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
index 5c17fd1..3bda775 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
@@ -20,9 +20,9 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
 
 @Composable
@@ -30,8 +30,8 @@
     viewModel: EditModeViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val gridLayout by viewModel.gridLayout.collectAsState()
-    val tiles by viewModel.tiles.collectAsState(emptyList())
+    val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle()
+    val tiles by viewModel.tiles.collectAsStateWithLifecycle(emptyList())
 
     BackHandler { viewModel.stopEditing() }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
index dc43091..f5ee720 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
@@ -16,85 +16,23 @@
 
 package com.android.systemui.qs.panels.ui.compose
 
-import android.graphics.drawable.Animatable
-import android.text.TextUtils
-import androidx.appcompat.content.res.AppCompatResources
-import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
-import androidx.compose.animation.graphics.res.animatedVectorResource
-import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
-import androidx.compose.animation.graphics.vector.AnimatedImageVector
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
-import androidx.compose.foundation.basicMarquee
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Arrangement.spacedBy
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxHeight
-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.lazy.grid.GridCells
 import androidx.compose.foundation.lazy.grid.GridItemSpan
-import androidx.compose.foundation.lazy.grid.LazyGridScope
-import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
-import androidx.compose.foundation.shape.CircleShape
-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.Remove
-import androidx.compose.material3.Icon
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.contentDescription
-import androidx.compose.ui.semantics.onClick
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.semantics.stateDescription
-import androidx.compose.ui.unit.dp
-import com.android.compose.animation.Expandable
-import com.android.compose.theme.colorAttr
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.ui.compose.Icon
-import com.android.systemui.common.ui.compose.load
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.panels.domain.interactor.IconTilesInteractor
 import com.android.systemui.qs.panels.domain.interactor.InfiniteGridSizeInteractor
-import com.android.systemui.qs.panels.ui.viewmodel.ActiveTileColorAttributes
-import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.TileColorAttributes
-import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
 import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.toUiState
-import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.res.R
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.mapLatest
 
 @SysUISingleton
 class InfiniteGridLayout
@@ -104,8 +42,6 @@
     private val gridSizeInteractor: InfiniteGridSizeInteractor
 ) : GridLayout {
 
-    private object TileType
-
     @Composable
     override fun TileGrid(
         tiles: List<TileViewModel>,
@@ -116,8 +52,8 @@
             tiles.forEach { it.startListening(token) }
             onDispose { tiles.forEach { it.stopListening(token) } }
         }
-        val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsState()
-        val columns by gridSizeInteractor.columns.collectAsState()
+        val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
+        val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
 
         TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
             items(
@@ -140,55 +76,6 @@
         }
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class)
-    @Composable
-    private fun Tile(
-        tile: TileViewModel,
-        iconOnly: Boolean,
-        modifier: Modifier,
-    ) {
-        val state: TileUiState by
-            tile.state
-                .mapLatest { it.toUiState() }
-                .collectAsState(initial = tile.currentState.toUiState())
-        val context = LocalContext.current
-
-        Expandable(
-            color = colorAttr(state.colors.background),
-            shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)),
-        ) {
-            Row(
-                modifier =
-                    modifier
-                        .combinedClickable(
-                            onClick = { tile.onClick(it) },
-                            onLongClick = { tile.onLongClick(it) }
-                        )
-                        .tileModifier(state.colors),
-                verticalAlignment = Alignment.CenterVertically,
-                horizontalArrangement = tileHorizontalArrangement(iconOnly),
-            ) {
-                val icon =
-                    remember(state.icon) {
-                        state.icon.get().let {
-                            if (it is QSTileImpl.ResourceIcon) {
-                                Icon.Resource(it.resId, null)
-                            } else {
-                                Icon.Loaded(it.getDrawable(context), null)
-                            }
-                        }
-                    }
-                TileContent(
-                    label = state.label.toString(),
-                    secondaryLabel = state.secondaryLabel?.toString(),
-                    icon = icon,
-                    colors = state.colors,
-                    iconOnly = iconOnly
-                )
-            }
-        }
-    }
-
     @Composable
     override fun EditTileGrid(
         tiles: List<EditTileViewModel>,
@@ -196,259 +83,16 @@
         onAddTile: (TileSpec, Int) -> Unit,
         onRemoveTile: (TileSpec) -> Unit,
     ) {
-        val (currentTiles, otherTiles) = tiles.partition { it.isCurrent }
-        val (otherTilesStock, otherTilesCustom) = otherTiles.partition { it.appName == null }
-        val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
-            onAddTile(it, POSITION_AT_END)
-        }
-        val iconOnlySpecs by iconTilesInteractor.iconTilesSpecs.collectAsState(initial = emptySet())
-        val isIconOnly: (TileSpec) -> Boolean =
-            remember(iconOnlySpecs) { { tileSpec: TileSpec -> tileSpec in iconOnlySpecs } }
-        val columns by gridSizeInteractor.columns.collectAsState()
+        val iconOnlySpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
+        val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
 
-        TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
-            // These Text are just placeholders to see the different sections. Not final UI.
-            item(span = { GridItemSpan(maxLineSpan) }) {
-                Text("Current tiles", color = Color.White)
-            }
-
-            editTiles(
-                currentTiles,
-                ClickAction.REMOVE,
-                onRemoveTile,
-                isIconOnly,
-                indicatePosition = true,
-            )
-
-            item(span = { GridItemSpan(maxLineSpan) }) { Text("Tiles to add", color = Color.White) }
-
-            editTiles(
-                otherTilesStock,
-                ClickAction.ADD,
-                addTileToEnd,
-                isIconOnly,
-            )
-
-            item(span = { GridItemSpan(maxLineSpan) }) {
-                Text("Custom tiles to add", color = Color.White)
-            }
-
-            editTiles(
-                otherTilesCustom,
-                ClickAction.ADD,
-                addTileToEnd,
-                isIconOnly,
-            )
-        }
-    }
-
-    private fun LazyGridScope.editTiles(
-        tiles: List<EditTileViewModel>,
-        clickAction: ClickAction,
-        onClick: (TileSpec) -> Unit,
-        isIconOnly: (TileSpec) -> Boolean,
-        indicatePosition: Boolean = false,
-    ) {
-        items(
-            count = tiles.size,
-            key = { tiles[it].tileSpec.spec },
-            span = { GridItemSpan(if (isIconOnly(tiles[it].tileSpec)) 1 else 2) },
-            contentType = { TileType }
-        ) {
-            val viewModel = tiles[it]
-            val canClick =
-                when (clickAction) {
-                    ClickAction.ADD -> AvailableEditActions.ADD in viewModel.availableEditActions
-                    ClickAction.REMOVE ->
-                        AvailableEditActions.REMOVE in viewModel.availableEditActions
-                }
-            val onClickActionName =
-                when (clickAction) {
-                    ClickAction.ADD ->
-                        stringResource(id = R.string.accessibility_qs_edit_tile_add_action)
-                    ClickAction.REMOVE ->
-                        stringResource(id = R.string.accessibility_qs_edit_remove_tile_action)
-                }
-            val stateDescription =
-                if (indicatePosition) {
-                    stringResource(id = R.string.accessibility_qs_edit_position, it + 1)
-                } else {
-                    ""
-                }
-
-            Box(
-                modifier =
-                    Modifier.clickable(enabled = canClick) { onClick.invoke(viewModel.tileSpec) }
-                        .animateItem()
-                        .semantics {
-                            onClick(onClickActionName) { false }
-                            this.stateDescription = stateDescription
-                        }
-            ) {
-                EditTile(
-                    tileViewModel = viewModel,
-                    isIconOnly(viewModel.tileSpec),
-                    modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
-                )
-                if (canClick) {
-                    Badge(clickAction, Modifier.align(Alignment.TopEnd))
-                }
-            }
-        }
-    }
-
-    @Composable
-    private fun Badge(action: ClickAction, modifier: Modifier = Modifier) {
-        Box(modifier = modifier.size(16.dp).background(Color.Cyan, shape = CircleShape)) {
-            Icon(
-                imageVector =
-                    when (action) {
-                        ClickAction.ADD -> Icons.Filled.Add
-                        ClickAction.REMOVE -> Icons.Filled.Remove
-                    },
-                "",
-                tint = Color.Black,
-            )
-        }
-    }
-
-    @Composable
-    private fun EditTile(
-        tileViewModel: EditTileViewModel,
-        iconOnly: Boolean,
-        modifier: Modifier = Modifier,
-    ) {
-        val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec
-        val colors = ActiveTileColorAttributes
-
-        Row(
-            modifier = modifier.tileModifier(colors).semantics { this.contentDescription = label },
-            verticalAlignment = Alignment.CenterVertically,
-            horizontalArrangement = tileHorizontalArrangement(iconOnly)
-        ) {
-            TileContent(
-                label = label,
-                secondaryLabel = tileViewModel.appName?.load(),
-                colors = colors,
-                icon = tileViewModel.icon,
-                iconOnly = iconOnly,
-                animateIconToEnd = true,
-            )
-        }
-    }
-
-    private enum class ClickAction {
-        ADD,
-        REMOVE,
-    }
-}
-
-@OptIn(ExperimentalAnimationGraphicsApi::class)
-@Composable
-private fun TileIcon(
-    icon: Icon,
-    color: Color,
-    animateToEnd: Boolean = false,
-) {
-    val modifier = Modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
-    val context = LocalContext.current
-    val loadedDrawable =
-        remember(icon, context) {
-            when (icon) {
-                is Icon.Loaded -> icon.drawable
-                is Icon.Resource -> AppCompatResources.getDrawable(context, icon.res)
-            }
-        }
-    if (loadedDrawable !is Animatable) {
-        Icon(
-            icon = icon,
-            tint = color,
+        DefaultEditTileGrid(
+            tiles = tiles,
+            iconOnlySpecs = iconOnlySpecs,
+            columns = GridCells.Fixed(columns),
             modifier = modifier,
+            onAddTile = onAddTile,
+            onRemoveTile = onRemoveTile,
         )
-    } else if (icon is Icon.Resource) {
-        val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
-        val painter =
-            if (animateToEnd) {
-                rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true)
-            } else {
-                var atEnd by remember(icon.res) { mutableStateOf(false) }
-                LaunchedEffect(key1 = icon.res) {
-                    delay(350)
-                    atEnd = true
-                }
-                rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd)
-            }
-        Image(
-            painter = painter,
-            contentDescription = null,
-            colorFilter = ColorFilter.tint(color = color),
-            modifier = modifier
-        )
-    }
-}
-
-@Composable
-private fun TileLazyGrid(
-    modifier: Modifier = Modifier,
-    columns: GridCells,
-    content: LazyGridScope.() -> Unit,
-) {
-    LazyVerticalGrid(
-        columns = columns,
-        verticalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
-        horizontalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_horizontal)),
-        modifier = modifier,
-        content = content,
-    )
-}
-
-@Composable
-private fun Modifier.tileModifier(colors: TileColorAttributes): Modifier {
-    return fillMaxWidth()
-        .clip(RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)))
-        .background(colorAttr(colors.background))
-        .padding(horizontal = dimensionResource(id = R.dimen.qs_label_container_margin))
-}
-
-@Composable
-private fun tileHorizontalArrangement(iconOnly: Boolean): Arrangement.Horizontal {
-    val horizontalAlignment =
-        if (iconOnly) {
-            Alignment.CenterHorizontally
-        } else {
-            Alignment.Start
-        }
-    return spacedBy(
-        space = dimensionResource(id = R.dimen.qs_label_container_margin),
-        alignment = horizontalAlignment
-    )
-}
-
-@Composable
-private fun TileContent(
-    label: String,
-    secondaryLabel: String?,
-    icon: Icon,
-    colors: TileColorAttributes,
-    iconOnly: Boolean,
-    animateIconToEnd: Boolean = false,
-) {
-    TileIcon(icon, colorAttr(colors.icon), animateIconToEnd)
-
-    if (!iconOnly) {
-        Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
-            Text(
-                label,
-                color = colorAttr(colors.label),
-                modifier = Modifier.basicMarquee(),
-            )
-            if (!TextUtils.isEmpty(secondaryLabel)) {
-                Text(
-                    secondaryLabel ?: "",
-                    color = colorAttr(colors.secondaryLabel),
-                    modifier = Modifier.basicMarquee(),
-                )
-            }
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
new file mode 100644
index 0000000..ddd97c2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose
+
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.dimensionResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.domain.interactor.IconTilesInteractor
+import com.android.systemui.qs.panels.domain.interactor.InfiniteGridSizeInteractor
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.shared.model.TileRow
+import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+@SysUISingleton
+class StretchedGridLayout
+@Inject
+constructor(
+    private val iconTilesInteractor: IconTilesInteractor,
+    private val gridSizeInteractor: InfiniteGridSizeInteractor,
+) : GridLayout {
+
+    @Composable
+    override fun TileGrid(
+        tiles: List<TileViewModel>,
+        modifier: Modifier,
+    ) {
+        DisposableEffect(tiles) {
+            val token = Any()
+            tiles.forEach { it.startListening(token) }
+            onDispose { tiles.forEach { it.stopListening(token) } }
+        }
+
+        // Tile widths [normal|stretched]
+        // Icon [3 | 4]
+        // Large [6 | 8]
+        val columns = 12
+        val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
+        val stretchedTiles =
+            remember(tiles) {
+                val sizedTiles =
+                    tiles.map {
+                        SizedTile(
+                            it,
+                            if (iconTilesSpecs.contains(it.spec)) {
+                                3
+                            } else {
+                                6
+                            }
+                        )
+                    }
+                splitInRows(sizedTiles, columns)
+            }
+
+        TileLazyGrid(columns = GridCells.Fixed(columns), modifier = modifier) {
+            items(stretchedTiles.size, span = { GridItemSpan(stretchedTiles[it].width) }) { index ->
+                Tile(
+                    stretchedTiles[index].tile,
+                    iconTilesSpecs.contains(stretchedTiles[index].tile.spec),
+                    Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+                )
+            }
+        }
+    }
+
+    @Composable
+    override fun EditTileGrid(
+        tiles: List<EditTileViewModel>,
+        modifier: Modifier,
+        onAddTile: (TileSpec, Int) -> Unit,
+        onRemoveTile: (TileSpec) -> Unit
+    ) {
+        val iconOnlySpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
+        val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
+
+        DefaultEditTileGrid(
+            tiles = tiles,
+            iconOnlySpecs = iconOnlySpecs,
+            columns = GridCells.Fixed(columns),
+            modifier = modifier,
+            onAddTile = onAddTile,
+            onRemoveTile = onRemoveTile,
+        )
+    }
+
+    private fun splitInRows(
+        tiles: List<SizedTile<TileViewModel>>,
+        columns: Int
+    ): List<SizedTile<TileViewModel>> {
+        val row = TileRow<TileViewModel>(columns)
+
+        return buildList {
+            for (tile in tiles) {
+                if (row.maybeAddTile(tile)) {
+                    if (row.isFull()) {
+                        // Row is full, no need to stretch tiles
+                        addAll(row.tiles)
+                        row.clear()
+                    }
+                } else {
+                    if (row.isFull()) {
+                        addAll(row.tiles)
+                    } else {
+                        // Stretching tiles when row isn't full
+                        addAll(row.tiles.map { it.copy(width = it.width + (it.width / 3)) })
+                    }
+                    row.clear()
+                    row.maybeAddTile(tile)
+                }
+            }
+            addAll(row.tiles)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
new file mode 100644
index 0000000..eb45110
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose
+
+import android.graphics.drawable.Animatable
+import android.text.TextUtils
+import androidx.appcompat.content.res.AppCompatResources
+import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
+import androidx.compose.animation.graphics.res.animatedVectorResource
+import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.basicMarquee
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Arrangement.spacedBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+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.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.foundation.lazy.grid.LazyGridScope
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.shape.CircleShape
+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.Remove
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.onClick
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.Expandable
+import com.android.compose.theme.colorAttr
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.common.ui.compose.load
+import com.android.systemui.qs.panels.ui.viewmodel.ActiveTileColorAttributes
+import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions
+import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.TileColorAttributes
+import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
+import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.toUiState
+import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.res.R
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.mapLatest
+
+object TileType
+
+@OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class)
+@Composable
+fun Tile(
+    tile: TileViewModel,
+    iconOnly: Boolean,
+    modifier: Modifier,
+) {
+    val state: TileUiState by
+        tile.state
+            .mapLatest { it.toUiState() }
+            .collectAsStateWithLifecycle(tile.currentState.toUiState())
+    val context = LocalContext.current
+
+    Expandable(
+        color = colorAttr(state.colors.background),
+        shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)),
+    ) {
+        Row(
+            modifier =
+                modifier
+                    .combinedClickable(
+                        onClick = { tile.onClick(it) },
+                        onLongClick = { tile.onLongClick(it) }
+                    )
+                    .tileModifier(state.colors),
+            verticalAlignment = Alignment.CenterVertically,
+            horizontalArrangement = tileHorizontalArrangement(iconOnly),
+        ) {
+            val icon =
+                remember(state.icon) {
+                    state.icon.get().let {
+                        if (it is QSTileImpl.ResourceIcon) {
+                            Icon.Resource(it.resId, null)
+                        } else {
+                            Icon.Loaded(it.getDrawable(context), null)
+                        }
+                    }
+                }
+            TileContent(
+                label = state.label.toString(),
+                secondaryLabel = state.secondaryLabel.toString(),
+                icon = icon,
+                colors = state.colors,
+                iconOnly = iconOnly
+            )
+        }
+    }
+}
+
+@Composable
+fun TileLazyGrid(
+    modifier: Modifier = Modifier,
+    columns: GridCells,
+    content: LazyGridScope.() -> Unit,
+) {
+    LazyVerticalGrid(
+        columns = columns,
+        verticalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
+        horizontalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_horizontal)),
+        modifier = modifier,
+        content = content,
+    )
+}
+
+@Composable
+fun DefaultEditTileGrid(
+    tiles: List<EditTileViewModel>,
+    iconOnlySpecs: Set<TileSpec>,
+    columns: GridCells,
+    modifier: Modifier,
+    onAddTile: (TileSpec, Int) -> Unit,
+    onRemoveTile: (TileSpec) -> Unit,
+) {
+    val (currentTiles, otherTiles) = tiles.partition { it.isCurrent }
+    val (otherTilesStock, otherTilesCustom) = otherTiles.partition { it.appName == null }
+    val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
+        onAddTile(it, CurrentTilesInteractor.POSITION_AT_END)
+    }
+    val isIconOnly: (TileSpec) -> Boolean =
+        remember(iconOnlySpecs) { { tileSpec: TileSpec -> tileSpec in iconOnlySpecs } }
+
+    TileLazyGrid(modifier = modifier, columns = columns) {
+        // These Text are just placeholders to see the different sections. Not final UI.
+        item(span = { GridItemSpan(maxLineSpan) }) { Text("Current tiles", color = Color.White) }
+
+        editTiles(
+            currentTiles,
+            ClickAction.REMOVE,
+            onRemoveTile,
+            isIconOnly,
+            indicatePosition = true,
+        )
+
+        item(span = { GridItemSpan(maxLineSpan) }) { Text("Tiles to add", color = Color.White) }
+
+        editTiles(
+            otherTilesStock,
+            ClickAction.ADD,
+            addTileToEnd,
+            isIconOnly,
+        )
+
+        item(span = { GridItemSpan(maxLineSpan) }) {
+            Text("Custom tiles to add", color = Color.White)
+        }
+
+        editTiles(
+            otherTilesCustom,
+            ClickAction.ADD,
+            addTileToEnd,
+            isIconOnly,
+        )
+    }
+}
+
+private fun LazyGridScope.editTiles(
+    tiles: List<EditTileViewModel>,
+    clickAction: ClickAction,
+    onClick: (TileSpec) -> Unit,
+    isIconOnly: (TileSpec) -> Boolean,
+    indicatePosition: Boolean = false,
+) {
+    items(
+        count = tiles.size,
+        key = { tiles[it].tileSpec.spec },
+        span = { GridItemSpan(if (isIconOnly(tiles[it].tileSpec)) 1 else 2) },
+        contentType = { TileType }
+    ) {
+        val viewModel = tiles[it]
+        val canClick =
+            when (clickAction) {
+                ClickAction.ADD -> AvailableEditActions.ADD in viewModel.availableEditActions
+                ClickAction.REMOVE -> AvailableEditActions.REMOVE in viewModel.availableEditActions
+            }
+        val onClickActionName =
+            when (clickAction) {
+                ClickAction.ADD ->
+                    stringResource(id = R.string.accessibility_qs_edit_tile_add_action)
+                ClickAction.REMOVE ->
+                    stringResource(id = R.string.accessibility_qs_edit_remove_tile_action)
+            }
+        val stateDescription =
+            if (indicatePosition) {
+                stringResource(id = R.string.accessibility_qs_edit_position, it + 1)
+            } else {
+                ""
+            }
+
+        Box(
+            modifier =
+                Modifier.clickable(enabled = canClick) { onClick.invoke(viewModel.tileSpec) }
+                    .animateItem()
+                    .semantics {
+                        onClick(onClickActionName) { false }
+                        this.stateDescription = stateDescription
+                    }
+        ) {
+            EditTile(
+                tileViewModel = viewModel,
+                isIconOnly(viewModel.tileSpec),
+                modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+            )
+            if (canClick) {
+                Badge(clickAction, Modifier.align(Alignment.TopEnd))
+            }
+        }
+    }
+}
+
+@Composable
+fun Badge(action: ClickAction, modifier: Modifier = Modifier) {
+    Box(modifier = modifier.size(16.dp).background(Color.Cyan, shape = CircleShape)) {
+        Icon(
+            imageVector =
+                when (action) {
+                    ClickAction.ADD -> Icons.Filled.Add
+                    ClickAction.REMOVE -> Icons.Filled.Remove
+                },
+            "",
+            tint = Color.Black,
+        )
+    }
+}
+
+@Composable
+fun EditTile(
+    tileViewModel: EditTileViewModel,
+    iconOnly: Boolean,
+    modifier: Modifier = Modifier,
+) {
+    val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec
+    val colors = ActiveTileColorAttributes
+
+    Row(
+        modifier = modifier.tileModifier(colors).semantics { this.contentDescription = label },
+        verticalAlignment = Alignment.CenterVertically,
+        horizontalArrangement = tileHorizontalArrangement(iconOnly)
+    ) {
+        TileContent(
+            label = label,
+            secondaryLabel = tileViewModel.appName?.load(),
+            colors = colors,
+            icon = tileViewModel.icon,
+            iconOnly = iconOnly,
+            animateIconToEnd = true,
+        )
+    }
+}
+
+enum class ClickAction {
+    ADD,
+    REMOVE,
+}
+
+@OptIn(ExperimentalAnimationGraphicsApi::class)
+@Composable
+private fun TileIcon(
+    icon: Icon,
+    color: Color,
+    animateToEnd: Boolean = false,
+) {
+    val modifier = Modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
+    val context = LocalContext.current
+    val loadedDrawable =
+        remember(icon, context) {
+            when (icon) {
+                is Icon.Loaded -> icon.drawable
+                is Icon.Resource -> AppCompatResources.getDrawable(context, icon.res)
+            }
+        }
+    if (loadedDrawable !is Animatable) {
+        Icon(
+            icon = icon,
+            tint = color,
+            modifier = modifier,
+        )
+    } else if (icon is Icon.Resource) {
+        val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
+        val painter =
+            if (animateToEnd) {
+                rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true)
+            } else {
+                var atEnd by remember(icon.res) { mutableStateOf(false) }
+                LaunchedEffect(key1 = icon.res) {
+                    delay(350)
+                    atEnd = true
+                }
+                rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd)
+            }
+        Image(
+            painter = painter,
+            contentDescription = null,
+            colorFilter = ColorFilter.tint(color = color),
+            modifier = modifier
+        )
+    }
+}
+
+@Composable
+private fun Modifier.tileModifier(colors: TileColorAttributes): Modifier {
+    return fillMaxWidth()
+        .clip(RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)))
+        .background(colorAttr(colors.background))
+        .padding(horizontal = dimensionResource(id = R.dimen.qs_label_container_margin))
+}
+
+@Composable
+private fun tileHorizontalArrangement(iconOnly: Boolean): Arrangement.Horizontal {
+    val horizontalAlignment =
+        if (iconOnly) {
+            Alignment.CenterHorizontally
+        } else {
+            Alignment.Start
+        }
+    return spacedBy(
+        space = dimensionResource(id = R.dimen.qs_label_container_margin),
+        alignment = horizontalAlignment
+    )
+}
+
+@Composable
+private fun TileContent(
+    label: String,
+    secondaryLabel: String?,
+    icon: Icon,
+    colors: TileColorAttributes,
+    iconOnly: Boolean,
+    animateIconToEnd: Boolean = false,
+) {
+    TileIcon(icon, colorAttr(colors.icon), animateIconToEnd)
+
+    if (!iconOnly) {
+        Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
+            Text(
+                label,
+                color = colorAttr(colors.label),
+                modifier = Modifier.basicMarquee(),
+            )
+            if (!TextUtils.isEmpty(secondaryLabel)) {
+                Text(
+                    secondaryLabel ?: "",
+                    color = colorAttr(colors.secondaryLabel),
+                    modifier = Modifier.basicMarquee(),
+                )
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
index 2f32d72..2dab7c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
@@ -17,15 +17,15 @@
 package com.android.systemui.qs.panels.ui.compose
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
 
 @Composable
 fun TileGrid(viewModel: TileGridViewModel, modifier: Modifier = Modifier) {
-    val gridLayout by viewModel.gridLayout.collectAsState()
-    val tiles by viewModel.tileViewModels.collectAsState(emptyList())
+    val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle()
+    val tiles by viewModel.tileViewModels.collectAsStateWithLifecycle(emptyList())
 
     gridLayout.TileGrid(tiles, modifier)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 2068799..71b69c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs.tiles;
 
+import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_AIRPLANE_MODE;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -32,9 +34,12 @@
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.telephony.flags.Flags;
+import com.android.settingslib.satellite.SatelliteDialogUtils;
 import com.android.systemui.animation.Expandable;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -54,6 +59,8 @@
 
 import dagger.Lazy;
 
+import kotlinx.coroutines.Job;
+
 import javax.inject.Inject;
 
 /** Quick settings tile: Airplane mode **/
@@ -66,6 +73,9 @@
     private final Lazy<ConnectivityManager> mLazyConnectivityManager;
 
     private boolean mListening;
+    @Nullable
+    @VisibleForTesting
+    Job mClickJob;
 
     @Inject
     public AirplaneModeTile(
@@ -111,6 +121,21 @@
                     new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS), 0);
             return;
         }
+
+        if (Flags.oemEnabledSatelliteFlag()) {
+            if (mClickJob != null && !mClickJob.isCompleted()) {
+                return;
+            }
+            mClickJob = SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+                    mContext, this, TYPE_IS_AIRPLANE_MODE, isAllowClick -> {
+                        if (isAllowClick) {
+                            setEnabled(!airplaneModeEnabled);
+                        }
+                        return null;
+                    });
+            return;
+        }
+
         setEnabled(!airplaneModeEnabled);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 9af34f6..9f41d98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles;
 
+import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_BLUETOOTH;
 import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
 
 import android.annotation.Nullable;
@@ -33,11 +34,14 @@
 import android.util.Log;
 import android.widget.Switch;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.satellite.SatelliteDialogUtils;
 import com.android.systemui.animation.Expandable;
 import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -55,6 +59,8 @@
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.BluetoothController;
 
+import kotlinx.coroutines.Job;
+
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -78,6 +84,9 @@
     private final BluetoothTileDialogViewModel mDialogViewModel;
 
     private final FeatureFlags mFeatureFlags;
+    @Nullable
+    @VisibleForTesting
+    Job mClickJob;
 
     @Inject
     public BluetoothTile(
@@ -110,6 +119,24 @@
 
     @Override
     protected void handleClick(@Nullable Expandable expandable) {
+        if (com.android.internal.telephony.flags.Flags.oemEnabledSatelliteFlag()) {
+            if (mClickJob != null && !mClickJob.isCompleted()) {
+                return;
+            }
+            mClickJob = SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+                    mContext, this, TYPE_IS_BLUETOOTH, isAllowClick -> {
+                        if (!isAllowClick) {
+                            return null;
+                        }
+                        handleClickEvent(expandable);
+                        return null;
+                    });
+            return;
+        }
+        handleClickEvent(expandable);
+    }
+
+    private void handleClickEvent(@Nullable Expandable expandable) {
         if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) {
             mDialogViewModel.showDialog(expandable);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
index 2d3120a..972b20e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
@@ -29,11 +29,15 @@
 
 /**
  * Provides a shortcut to start an activity from [QSTileUserActionInteractor]. It supports keyguard
- * dismissing and tile from-view animations.
+ * dismissing and tile from-view animations, as well as the option to show over lockscreen.
  */
 interface QSTileIntentUserInputHandler {
 
-    fun handle(expandable: Expandable?, intent: Intent)
+    fun handle(
+        expandable: Expandable?,
+        intent: Intent,
+        dismissShadeShowOverLockScreenWhenLocked: Boolean = false
+    )
 
     /** @param requestLaunchingDefaultActivity used in case !pendingIndent.isActivity */
     fun handle(
@@ -52,12 +56,25 @@
     private val userHandle: UserHandle,
 ) : QSTileIntentUserInputHandler {
 
-    override fun handle(expandable: Expandable?, intent: Intent) {
+    override fun handle(
+        expandable: Expandable?,
+        intent: Intent,
+        dismissShadeShowOverLockScreenWhenLocked: Boolean
+    ) {
         val animationController: ActivityTransitionAnimator.Controller? =
             expandable?.activityTransitionController(
                 InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
             )
-        activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController)
+        if (dismissShadeShowOverLockScreenWhenLocked) {
+            activityStarter.startActivity(
+                intent,
+                true /* dismissShade */,
+                animationController,
+                true /* showOverLockscreenWhenLocked */
+            )
+        } else {
+            activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController)
+        }
     }
 
     // TODO(b/249804373): make sure to allow showing activities over the lockscreen. See b/292112939
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index c9c4443..c971f54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.qs.tiles.dialog;
 
+import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI;
 import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
 import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
 
@@ -57,6 +58,8 @@
 
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
+import com.android.internal.telephony.flags.Flags;
+import com.android.settingslib.satellite.SatelliteDialogUtils;
 import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
 import com.android.systemui.Prefs;
 import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
@@ -73,6 +76,7 @@
 import dagger.assisted.AssistedInject;
 
 import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.Job;
 
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -161,6 +165,9 @@
     // Wi-Fi scanning progress bar
     protected boolean mIsProgressBarVisible;
     private SystemUIDialog mDialog;
+    private final CoroutineScope mCoroutineScope;
+    @Nullable
+    private Job mClickJob;
 
     @AssistedFactory
     public interface Factory {
@@ -203,7 +210,7 @@
         mCanConfigWifi = canConfigWifi;
         mCanChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context);
         mKeyguard = keyguardStateController;
-
+        mCoroutineScope = coroutineScope;
         mUiEventLogger = uiEventLogger;
         mDialogTransitionAnimator = dialogTransitionAnimator;
         mAdapter = new InternetAdapter(mInternetDialogController, coroutineScope);
@@ -388,11 +395,9 @@
         });
         mConnectedWifListLayout.setOnClickListener(this::onClickConnectedWifi);
         mSeeAllLayout.setOnClickListener(this::onClickSeeMoreButton);
-        mWiFiToggle.setOnCheckedChangeListener(
-                (buttonView, isChecked) -> {
-                    if (mInternetDialogController.isWifiEnabled() == isChecked) return;
-                    mInternetDialogController.setWifiEnabled(isChecked);
-                });
+        mWiFiToggle.setOnClickListener(v -> {
+            handleWifiToggleClicked(mWiFiToggle.isChecked());
+        });
         mDoneButton.setOnClickListener(v -> dialog.dismiss());
         mShareWifiButton.setOnClickListener(v -> {
             if (mInternetDialogController.mayLaunchShareWifiSettings(mConnectedWifiEntry, v)) {
@@ -404,6 +409,32 @@
         });
     }
 
+    private void handleWifiToggleClicked(boolean isChecked) {
+        if (Flags.oemEnabledSatelliteFlag()) {
+            if (mClickJob != null && !mClickJob.isCompleted()) {
+                return;
+            }
+            mClickJob = SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+                    mDialog.getContext(), mCoroutineScope, TYPE_IS_WIFI, isAllowClick -> {
+                        if (isAllowClick) {
+                            setWifiEnable(isChecked);
+                        } else {
+                            mWiFiToggle.setChecked(!isChecked);
+                        }
+                        return null;
+                    });
+            return;
+        }
+        setWifiEnable(isChecked);
+    }
+
+    private void setWifiEnable(boolean isChecked) {
+        if (mInternetDialogController.isWifiEnabled() == isChecked) {
+            return;
+        }
+        mInternetDialogController.setWifiEnabled(isChecked);
+    }
+
     @MainThread
     private void updateEthernet() {
         mEthernetLayout.setVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
new file mode 100644
index 0000000..1e8ce58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
+import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+
+/** Observes one qr scanner state changes providing the [QRCodeScannerTileModel]. */
+class QRCodeScannerTileDataInteractor
+@Inject
+constructor(
+    @Background private val bgCoroutineContext: CoroutineContext,
+    @Application private val scope: CoroutineScope,
+    private val qrController: QRCodeScannerController,
+) : QSTileDataInteractor<QRCodeScannerTileModel> {
+    override fun tileData(
+        user: UserHandle,
+        triggers: Flow<DataUpdateTrigger>
+    ): Flow<QRCodeScannerTileModel> =
+        conflatedCallbackFlow {
+                qrController.registerQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE)
+                val callback =
+                    object : QRCodeScannerController.Callback {
+                        override fun onQRCodeScannerActivityChanged() {
+                            trySend(generateModel())
+                        }
+                    }
+                qrController.addCallback(callback)
+                awaitClose {
+                    qrController.removeCallback(callback)
+                    qrController.unregisterQRCodeScannerChangeObservers(
+                        DEFAULT_QR_CODE_SCANNER_CHANGE
+                    )
+                }
+            }
+            .onStart { emit(generateModel()) }
+            .flowOn(bgCoroutineContext)
+            .stateIn(
+                scope,
+                SharingStarted.WhileSubscribed(),
+                QRCodeScannerTileModel.TemporarilyUnavailable
+            )
+
+    override fun availability(user: UserHandle): Flow<Boolean> =
+        flowOf(qrController.isCameraAvailable)
+
+    private fun generateModel(): QRCodeScannerTileModel {
+        val intent = qrController.intent
+
+        return if (qrController.isAbleToLaunchScannerActivity && intent != null)
+            QRCodeScannerTileModel.Available(intent)
+        else QRCodeScannerTileModel.TemporarilyUnavailable
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
new file mode 100644
index 0000000..7c0c41e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.domain.interactor
+
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+
+/** Handles qr tile clicks. */
+class QRCodeScannerTileUserActionInteractor
+@Inject
+constructor(
+    private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+) : QSTileUserActionInteractor<QRCodeScannerTileModel> {
+
+    override suspend fun handleInput(input: QSTileInput<QRCodeScannerTileModel>): Unit =
+        with(input) {
+            when (action) {
+                is QSTileUserAction.Click -> {
+                    when (data) {
+                        is QRCodeScannerTileModel.Available ->
+                            qsTileIntentUserActionHandler.handle(
+                                action.expandable,
+                                data.intent,
+                                true
+                            )
+                        is QRCodeScannerTileModel.TemporarilyUnavailable -> {} // no-op
+                    }
+                }
+                is QSTileUserAction.LongClick -> {} // no-op
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/model/QRCodeScannerTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/model/QRCodeScannerTileModel.kt
new file mode 100644
index 0000000..22c9b66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/model/QRCodeScannerTileModel.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.domain.model
+
+import android.content.Intent
+
+/** qr scanner tile model. */
+sealed interface QRCodeScannerTileModel {
+    data class Available(val intent: Intent) : QRCodeScannerTileModel
+    data object TemporarilyUnavailable : QRCodeScannerTileModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
new file mode 100644
index 0000000..45a7717
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.ui
+
+import android.content.res.Resources
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [QRCodeScannerTileModel] to [QSTileState]. */
+class QRCodeScannerTileMapper
+@Inject
+constructor(
+    @Main private val resources: Resources,
+    private val theme: Resources.Theme,
+) : QSTileDataToStateMapper<QRCodeScannerTileModel> {
+
+    override fun map(config: QSTileConfig, data: QRCodeScannerTileModel): QSTileState =
+        QSTileState.build(resources, theme, config.uiConfig) {
+            label = resources.getString(R.string.qr_code_scanner_title)
+            contentDescription = label
+            icon = {
+                Icon.Loaded(resources.getDrawable(R.drawable.ic_qr_code_scanner, theme), null)
+            }
+            sideViewIcon = QSTileState.SideViewIcon.Chevron
+            supportedActions = setOf(QSTileState.UserAction.CLICK)
+
+            when (data) {
+                is QRCodeScannerTileModel.Available -> {
+                    activationState = QSTileState.ActivationState.INACTIVE
+                    secondaryLabel = null
+                }
+                is QRCodeScannerTileModel.TemporarilyUnavailable -> {
+                    activationState = QSTileState.ActivationState.UNAVAILABLE
+                    secondaryLabel =
+                        resources.getString(R.string.qr_code_scanner_updating_secondary_label)
+                }
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index faf2bbc..0327ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -108,15 +108,18 @@
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
 import com.android.systemui.statusbar.policy.CallbackController;
 import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellInterface;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -128,8 +131,6 @@
 import javax.inject.Inject;
 import javax.inject.Provider;
 
-import dagger.Lazy;
-
 /**
  * Class to send information from overview to launcher with a binder.
  */
@@ -414,7 +415,13 @@
         @Override
         public void toggleNotificationPanel() {
             verifyCallerAndClearCallingIdentityPostMain("toggleNotificationPanel", () ->
-                    mCommandQueue.togglePanel());
+                    mCommandQueue.toggleNotificationsPanel());
+        }
+
+        @Override
+        public void toggleQuickSettingsPanel() {
+            verifyCallerAndClearCallingIdentityPostMain("toggleQuickSettingsPanel", () ->
+                    mCommandQueue.toggleQuickSettingsPanel());
         }
 
         private boolean verifyCaller(String reason) {
@@ -765,7 +772,7 @@
         }
     }
 
-    private void notifySystemUiStateFlags(int flags) {
+    private void notifySystemUiStateFlags(@SystemUiStateFlags long flags) {
         if (SysUiState.DEBUG) {
             Log.d(TAG_OPS, "Notifying sysui state change to overview service: proxy="
                     + mOverviewProxy + " flags=" + flags);
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index 4e290e6..6694878 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -20,7 +20,6 @@
 import android.app.NotificationManager
 import android.content.Context
 import android.content.Intent
-import android.content.pm.LauncherApps
 import android.content.res.Resources
 import android.net.Uri
 import android.os.Handler
@@ -63,7 +62,6 @@
     private val panelInteractor: PanelInteractor,
     private val issueRecordingState: IssueRecordingState,
     private val iActivityManager: IActivityManager,
-    private val launcherApps: LauncherApps,
 ) :
     RecordingService(
         controller,
@@ -85,7 +83,7 @@
         when (intent?.action) {
             ACTION_START -> {
                 TraceUtils.traceStart(
-                    contentResolver,
+                    this,
                     DEFAULT_TRACE_TAGS,
                     DEFAULT_BUFFER_SIZE,
                     DEFAULT_IS_INCLUDING_WINSCOPE,
@@ -104,11 +102,7 @@
             }
             ACTION_STOP,
             ACTION_STOP_NOTIF -> {
-                // ViewCapture needs to save it's data before it is disabled, or else the data will
-                // be lost. This is expected to change in the near future, and when that happens
-                // this line should be removed.
-                launcherApps.saveViewCaptureData()
-                TraceUtils.traceStop(contentResolver)
+                TraceUtils.traceStop(this)
                 issueRecordingState.isRecording = false
             }
             ACTION_SHARE -> {
@@ -142,7 +136,7 @@
 
     private fun shareRecording(screenRecording: Uri?) {
         val traces =
-            TraceUtils.traceDump(contentResolver, TRACE_FILE_NAME).getOrElse {
+            TraceUtils.traceDump(this, TRACE_FILE_NAME).getOrElse {
                 Log.v(
                     TAG,
                     "Traces were not present. This can happen if users double" +
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index eabc42b..3e2c630 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -21,6 +21,7 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.SceneDataSource
@@ -36,6 +37,7 @@
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
 
+@SysUISingleton
 /** Source of truth for scene framework application state. */
 class SceneContainerRepository
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 08efe39..0d0f6e0 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -55,6 +55,18 @@
     private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
 ) {
 
+    interface OnSceneAboutToChangeListener {
+
+        /**
+         * Notifies that the scene is about to change to [toScene].
+         *
+         * The implementation can choose to consume the [sceneState] to prepare the incoming scene.
+         */
+        fun onSceneAboutToChange(toScene: SceneKey, sceneState: Any?)
+    }
+
+    private val onSceneAboutToChangeListener = mutableSetOf<OnSceneAboutToChangeListener>()
+
     /**
      * The current scene.
      *
@@ -149,6 +161,10 @@
         return repository.allSceneKeys()
     }
 
+    fun registerSceneStateProcessor(processor: OnSceneAboutToChangeListener) {
+        onSceneAboutToChangeListener.add(processor)
+    }
+
     /**
      * Requests a scene change to the given scene.
      *
@@ -161,6 +177,7 @@
         toScene: SceneKey,
         loggingReason: String,
         transitionKey: TransitionKey? = null,
+        sceneState: Any? = null,
     ) {
         val currentSceneKey = currentScene.value
         if (
@@ -180,6 +197,7 @@
             isInstant = false,
         )
 
+        onSceneAboutToChangeListener.forEach { it.onSceneAboutToChange(toScene, sceneState) }
         repository.changeScene(toScene, transitionKey)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 4a64277..3ce12dd 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -234,7 +234,7 @@
             bouncerInteractor.onImeHiddenByUser.collectLatest {
                 if (sceneInteractor.currentScene.value == Scenes.Bouncer) {
                     sceneInteractor.changeScene(
-                        toScene = Scenes.Lockscreen,
+                        toScene = Scenes.Lockscreen, // TODO(b/336581871): add sceneState?
                         loggingReason = "IME hidden",
                     )
                 }
@@ -252,6 +252,7 @@
                     when {
                         isAnySimLocked -> {
                             switchToScene(
+                                // TODO(b/336581871): add sceneState?
                                 targetSceneKey = Scenes.Bouncer,
                                 loggingReason = "Need to authenticate locked SIM card."
                             )
@@ -259,6 +260,7 @@
                         unlockStatus.isUnlocked &&
                             deviceEntryInteractor.canSwipeToEnter.value == false -> {
                             switchToScene(
+                                // TODO(b/336581871): add sceneState?
                                 targetSceneKey = Scenes.Gone,
                                 loggingReason =
                                     "All SIM cards unlocked and device already unlocked and " +
@@ -267,6 +269,7 @@
                         }
                         else -> {
                             switchToScene(
+                                // TODO(b/336581871): add sceneState?
                                 targetSceneKey = Scenes.Lockscreen,
                                 loggingReason =
                                     "All SIM cards unlocked and device still locked" +
@@ -325,7 +328,8 @@
                                 Scenes.Gone to "device was unlocked in Bouncer scene"
                             } else {
                                 val prevScene = previousScene.value
-                                (prevScene ?: Scenes.Gone) to
+                                (prevScene
+                                    ?: Scenes.Gone) to
                                     "device was unlocked in Bouncer scene, from sceneKey=$prevScene"
                             }
                         isOnLockscreen ->
@@ -364,6 +368,7 @@
             powerInteractor.isAsleep.collect { isAsleep ->
                 if (isAsleep) {
                     switchToScene(
+                        // TODO(b/336581871): add sceneState?
                         targetSceneKey = Scenes.Lockscreen,
                         loggingReason = "device is starting to sleep",
                     )
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
index 0603d21..ef393e4 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
@@ -24,8 +24,8 @@
  * These are the subset of transitions that can be referenced by key when asking for a scene change.
  */
 object TransitionKeys {
-    /** Reference to the gone to shade transition with split shade enabled. */
-    val GoneToSplitShade = TransitionKey("GoneToSplitShade")
+    /** Reference to the gone/lockscreen to shade transition with split shade enabled. */
+    val ToSplitShade = TransitionKey("GoneToSplitShade")
 
     /** Reference to a scene transition that can collapse the shade scene instantly. */
     val CollapseShadeInstantly = TransitionKey("CollapseShadeInstantly")
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
index 016fe57..99118bc 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.GoneToSplitShade
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
 import javax.inject.Inject
@@ -73,7 +73,7 @@
 
             val downSceneKey =
                 if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade
-            val downTransitionKey = GoneToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+            val downTransitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
             this[Swipe(direction = SwipeDirection.Down)] =
                 UserActionResult(downSceneKey, downTransitionKey)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
index caa67df..1868b4a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
@@ -25,7 +25,6 @@
 import android.os.UserHandle
 import android.util.Log
 import android.util.Pair
-import android.view.View
 import android.view.Window
 import com.android.app.tracing.coroutines.launch
 import com.android.internal.app.ChooserActivity
@@ -41,8 +40,8 @@
     private val intentExecutor: ActionIntentExecutor,
     @Application private val applicationScope: CoroutineScope,
     @Assisted val window: Window,
-    @Assisted val transitionView: View,
-    @Assisted val onDismiss: (() -> Unit)
+    @Assisted val viewProxy: ScreenshotViewProxy,
+    @Assisted val finishDismiss: () -> Unit,
 ) {
 
     var isPendingSharedTransition = false
@@ -50,6 +49,7 @@
 
     fun startSharedTransition(intent: Intent, user: UserHandle, overrideTransition: Boolean) {
         isPendingSharedTransition = true
+        viewProxy.fadeForSharedTransition()
         val windowTransition = createWindowTransition()
         applicationScope.launch("$TAG#launchIntentAsync") {
             intentExecutor.launchIntent(
@@ -70,7 +70,7 @@
                 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
             )
             pendingIntent.send(options.toBundle())
-            onDismiss.invoke()
+            viewProxy.requestDismissal(null)
         } catch (e: PendingIntent.CanceledException) {
             Log.e(TAG, "Intent cancelled", e)
         }
@@ -89,7 +89,7 @@
 
                 override fun hideSharedElements() {
                     isPendingSharedTransition = false
-                    onDismiss.invoke()
+                    finishDismiss.invoke()
                 }
 
                 override fun onFinish() {}
@@ -98,13 +98,20 @@
             window,
             callbacks,
             null,
-            Pair.create(transitionView, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)
+            Pair.create(
+                viewProxy.screenshotPreview,
+                ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME
+            )
         )
     }
 
     @AssistedFactory
     interface Factory {
-        fun create(window: Window, transitionView: View, onDismiss: (() -> Unit)): ActionExecutor
+        fun create(
+            window: Window,
+            viewProxy: ScreenshotViewProxy,
+            finishDismiss: (() -> Unit)
+        ): ActionExecutor
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
index a0cef52..15638d3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
@@ -97,6 +97,7 @@
             .putExtra(LongScreenshotActivity.EXTRA_SCREENSHOT_USER_HANDLE, owner)
             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
             .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+            .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
     }
 
     private const val EXTRA_EDIT_SOURCE = "edit_source"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 4eca51d..4ab0918 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -33,10 +33,11 @@
 import android.view.WindowManagerGlobal
 import com.android.app.tracing.coroutines.launch
 import com.android.internal.infra.ServiceConnector
-import com.android.systemui.Flags.screenshotActionDismissSystemWindows
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.screenshot.proxy.SystemUiProxy
 import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.shared.system.ActivityManagerWrapper
 import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -54,8 +55,8 @@
     private val activityManagerWrapper: ActivityManagerWrapper,
     @Application private val applicationScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
+    private val systemUiProxy: SystemUiProxy,
     private val displayTracker: DisplayTracker,
-    private val keyguardController: ScreenshotKeyguardController,
 ) {
     /**
      * Execute the given intent with startActivity while performing operations for screenshot action
@@ -83,14 +84,12 @@
         options: ActivityOptions?,
         transitionCoordinator: ExitTransitionCoordinator?,
     ) {
-        if (screenshotActionDismissSystemWindows()) {
-            keyguardController.dismiss()
+        if (Flags.fixScreenshotActionDismissSystemWindows()) {
             activityManagerWrapper.closeSystemWindows(
                 CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT
             )
-        } else {
-            dismissKeyguard()
         }
+        systemUiProxy.dismissKeyguard()
         transitionCoordinator?.startExit()
 
         if (user == myUserHandle()) {
@@ -110,27 +109,6 @@
         }
     }
 
-    private val proxyConnector: ServiceConnector<IScreenshotProxy> =
-        ServiceConnector.Impl(
-            context,
-            Intent(context, ScreenshotProxyService::class.java),
-            Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
-            context.userId,
-            IScreenshotProxy.Stub::asInterface,
-        )
-
-    private suspend fun dismissKeyguard() {
-        val completion = CompletableDeferred<Unit>()
-        val onDoneBinder =
-            object : IOnDoneCallback.Stub() {
-                override fun onDone(success: Boolean) {
-                    completion.complete(Unit)
-                }
-            }
-        proxyConnector.post { it.dismissKeyguard(onDoneBinder) }
-        completion.await()
-    }
-
     private fun getCrossProfileConnector(user: UserHandle): ServiceConnector<ICrossProfileService> =
         ServiceConnector.Impl<ICrossProfileService>(
             context,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
index 4cf18fb..3d024a6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
@@ -157,6 +157,8 @@
 
     override fun restoreNonScrollingUi() = view.restoreNonScrollingUi()
 
+    override fun fadeForSharedTransition() {} // unused
+
     override fun stopInputListening() = view.stopInputListening()
 
     override fun requestFocus() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
index ef1d87d..a1dd415 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -40,6 +40,7 @@
  */
 interface ScreenshotActionsProvider {
     fun onScrollChipReady(onClick: Runnable)
+    fun onScrollChipInvalidated()
     fun setCompletedScreenshot(result: ScreenshotSavedResult)
 
     /**
@@ -67,6 +68,8 @@
     @Assisted val requestId: String,
     @Assisted val actionExecutor: ActionExecutor,
 ) : ScreenshotActionsProvider {
+    private var addedScrollChip = false
+    private var onScrollClick: Runnable? = null
     private var pendingAction: ((ScreenshotSavedResult) -> Unit)? = null
     private var result: ScreenshotSavedResult? = null
 
@@ -122,18 +125,26 @@
     }
 
     override fun onScrollChipReady(onClick: Runnable) {
-        viewModel.addAction(
-            ActionButtonAppearance(
-                AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_scroll),
-                context.resources.getString(R.string.screenshot_scroll_label),
-                context.resources.getString(R.string.screenshot_scroll_label),
-            ),
-            showDuringEntrance = true,
-        ) {
-            onClick.run()
+        onScrollClick = onClick
+        if (!addedScrollChip) {
+            viewModel.addAction(
+                ActionButtonAppearance(
+                    AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_scroll),
+                    context.resources.getString(R.string.screenshot_scroll_label),
+                    context.resources.getString(R.string.screenshot_scroll_label),
+                ),
+                showDuringEntrance = true,
+            ) {
+                onScrollClick?.run()
+            }
+            addedScrollChip = true
         }
     }
 
+    override fun onScrollChipInvalidated() {
+        onScrollClick = null
+    }
+
     override fun setCompletedScreenshot(result: ScreenshotSavedResult) {
         if (this.result != null) {
             Log.e(TAG, "Got a second completed screenshot for existing request!")
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index bd90de2..9ad6d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -317,9 +317,9 @@
         mConfigChanges.applyNewConfig(context.getResources());
         reloadAssets();
 
-        mActionExecutor = actionExecutorFactory.create(mWindow, mViewProxy.getScreenshotPreview(),
+        mActionExecutor = actionExecutorFactory.create(mWindow, mViewProxy,
                 () -> {
-                    requestDismissal(null);
+                    finishDismiss();
                     return Unit.INSTANCE;
                 });
 
@@ -586,7 +586,11 @@
                             if (mConfigChanges.applyNewConfig(mContext.getResources())) {
                                 // Hide the scroll chip until we know it's available in this
                                 // orientation
-                                mViewProxy.hideScrollChip();
+                                if (screenshotShelfUi2()) {
+                                    mActionsProvider.onScrollChipInvalidated();
+                                } else {
+                                    mViewProxy.hideScrollChip();
+                                }
                                 // Delay scroll capture eval a bit to allow the underlying activity
                                 // to set up in the new orientation.
                                 mScreenshotHandler.postDelayed(
@@ -619,9 +623,11 @@
                 (response) -> {
                     mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION,
                             0, response.getPackageName());
-                    if (screenshotShelfUi2() && mActionsProvider != null) {
-                        mActionsProvider.onScrollChipReady(
-                                () -> onScrollButtonClicked(owner, response));
+                    if (screenshotShelfUi2()) {
+                        if (mActionsProvider != null) {
+                            mActionsProvider.onScrollChipReady(
+                                    () -> onScrollButtonClicked(owner, response));
+                        }
                     } else {
                         mViewProxy.showScrollChip(response.getPackageName(),
                                 () -> onScrollButtonClicked(owner, response));
@@ -653,9 +659,7 @@
                 () -> {
                     final Intent intent = ActionIntentCreator.INSTANCE.createLongScreenshotIntent(
                             owner, mContext);
-                    mActionIntentExecutor.launchIntentAsync(intent, owner, true,
-                            ActivityOptions.makeCustomAnimation(mContext, 0, 0), null);
-
+                    mActionIntentExecutor.launchIntentAsync(intent, owner, true, null, null);
                 },
                 mViewProxy::restoreNonScrollingUi,
                 mViewProxy::startLongScreenshotTransition);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt
deleted file mode 100644
index 7696bbe..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot
-
-import android.content.Context
-import android.content.Intent
-import com.android.internal.infra.ServiceConnector
-import javax.inject.Inject
-import kotlinx.coroutines.CompletableDeferred
-
-open class ScreenshotKeyguardController @Inject constructor(context: Context) {
-    private val proxyConnector: ServiceConnector<IScreenshotProxy> =
-        ServiceConnector.Impl(
-            context,
-            Intent(context, ScreenshotProxyService::class.java),
-            Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
-            context.userId,
-            IScreenshotProxy.Stub::asInterface
-        )
-
-    suspend fun dismiss() {
-        val completion = CompletableDeferred<Unit>()
-        val onDoneBinder =
-            object : IOnDoneCallback.Stub() {
-                override fun onDone(success: Boolean) {
-                    completion.complete(Unit)
-                }
-            }
-        proxyConnector.post { it.dismissKeyguard(onDoneBinder) }
-        completion.await()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
index 412b089..3ac070a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
@@ -31,6 +31,7 @@
 import android.view.WindowManager
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
+import androidx.appcompat.content.res.AppCompatResources
 import androidx.core.animation.doOnEnd
 import androidx.core.animation.doOnStart
 import com.android.internal.logging.UiEventLogger
@@ -58,6 +59,7 @@
     private val logger: UiEventLogger,
     private val viewModel: ScreenshotViewModel,
     private val windowManager: WindowManager,
+    shelfViewBinder: ScreenshotShelfViewBinder,
     private val thumbnailObserver: ThumbnailObserver,
     @Assisted private val context: Context,
     @Assisted private val displayId: Int
@@ -69,7 +71,17 @@
     override var callbacks: ScreenshotView.ScreenshotViewCallback? = null
     override var screenshot: ScreenshotData? = null
         set(value) {
-            viewModel.setScreenshotBitmap(value?.bitmap)
+            value?.let {
+                viewModel.setScreenshotBitmap(it.bitmap)
+                val badgeBg =
+                    AppCompatResources.getDrawable(context, R.drawable.overlay_badge_background)
+                val user = it.userHandle
+                if (badgeBg != null && user != null) {
+                    viewModel.setScreenshotBadge(
+                        context.packageManager.getUserBadgedIcon(badgeBg, user)
+                    )
+                }
+            }
             field = value
         }
 
@@ -78,15 +90,16 @@
     override var isDismissing = false
     override var isPendingSharedTransition = false
 
-    private val animationController = ScreenshotAnimationController(view)
+    private val animationController = ScreenshotAnimationController(view, viewModel)
 
     init {
-        ScreenshotShelfViewBinder.bind(
+        shelfViewBinder.bind(
             view,
             viewModel,
+            animationController,
             LayoutInflater.from(context),
             onDismissalRequested = { event, velocity -> requestDismissal(event, velocity) },
-            onDismissalCancelled = { animationController.getSwipeReturnAnimation().start() }
+            onUserInteraction = { callbacks?.onUserInteraction() }
         )
         view.updateInsets(windowManager.currentWindowMetrics.windowInsets)
         addPredictiveBackListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
@@ -175,24 +188,53 @@
 
     override fun prepareScrollingTransition(
         response: ScrollCaptureResponse,
-        screenBitmap: Bitmap,
+        screenBitmap: Bitmap, // unused
         newScreenshot: Bitmap,
         screenshotTakenInPortrait: Boolean,
         onTransitionPrepared: Runnable,
     ) {
-        onTransitionPrepared.run()
+        viewModel.setScrollingScrimBitmap(newScreenshot)
+        viewModel.setScrollableRect(scrollableAreaOnScreen(response))
+        animationController.fadeForLongScreenshotTransition()
+        view.post { onTransitionPrepared.run() }
+    }
+
+    private fun scrollableAreaOnScreen(response: ScrollCaptureResponse): Rect {
+        val r = Rect(response.boundsInWindow)
+        val windowInScreen = response.windowBounds
+        r.offset(windowInScreen?.left ?: 0, windowInScreen?.top ?: 0)
+        r.intersect(
+            Rect(
+                0,
+                0,
+                context.resources.displayMetrics.widthPixels,
+                context.resources.displayMetrics.heightPixels
+            )
+        )
+        return r
     }
 
     override fun startLongScreenshotTransition(
         transitionDestination: Rect,
         onTransitionEnd: Runnable,
-        longScreenshot: ScrollCaptureController.LongScreenshot
+        longScreenshot: ScrollCaptureController.LongScreenshot,
     ) {
-        onTransitionEnd.run()
-        callbacks?.onDismiss()
+        val transitionAnimation =
+            animationController.runLongScreenshotTransition(
+                transitionDestination,
+                longScreenshot,
+                onTransitionEnd
+            )
+        transitionAnimation.doOnEnd { callbacks?.onDismiss() }
+        transitionAnimation.start()
     }
 
-    override fun restoreNonScrollingUi() {}
+    override fun restoreNonScrollingUi() {
+        viewModel.setScrollableRect(null)
+        viewModel.setScrollingScrimBitmap(null)
+        animationController.restoreUI()
+        callbacks?.onUserInteraction() // reset the timeout
+    }
 
     override fun stopInputListening() {}
 
@@ -215,6 +257,10 @@
         )
     }
 
+    override fun fadeForSharedTransition() {
+        animationController.fadeForSharedTransition()
+    }
+
     private fun addPredictiveBackListener(onDismissRequested: (ScreenshotEvent) -> Unit) {
         val onBackInvokedCallback = OnBackInvokedCallback {
             debugLog(DEBUG_INPUT) { "Predictive Back callback dispatched" }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
index a4069d1..df93a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
@@ -63,6 +63,7 @@
         longScreenshot: ScrollCaptureController.LongScreenshot
     )
     fun restoreNonScrollingUi()
+    fun fadeForSharedTransition()
 
     fun stopInputListening()
     fun requestFocus()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
index 5e561cf..ee1944e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
@@ -45,6 +45,7 @@
 import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
 
 import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.Flags;
 import com.android.systemui.res.R;
 
 import java.util.List;
@@ -378,8 +379,14 @@
                 upper = 1;
                 break;
         }
-        Log.i(TAG, "getAllowedValues: " + boundary + ", "
-                + "result=[lower=" + lower + ", upper=" + upper + "]");
+        if (lower >= upper) {
+            Log.wtf(TAG, "getAllowedValues computed an invalid range "
+                    + "[" + lower + ", " + upper + "]");
+            if (Flags.screenshotScrollCropViewCrashFix()) {
+                lower = Math.min(lower, upper);
+                upper = lower;
+            }
+        }
         return new Range<>(lower, upper);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
index 06e88f4..a4906c1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
@@ -20,6 +20,10 @@
 import android.animation.AnimatorSet
 import android.animation.ObjectAnimator
 import android.animation.ValueAnimator
+import android.content.res.ColorStateList
+import android.graphics.BlendMode
+import android.graphics.Color
+import android.graphics.Matrix
 import android.graphics.PointF
 import android.graphics.Rect
 import android.util.MathUtils
@@ -29,13 +33,21 @@
 import androidx.core.animation.doOnEnd
 import androidx.core.animation.doOnStart
 import com.android.systemui.res.R
+import com.android.systemui.screenshot.scroll.ScrollCaptureController
+import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.sign
 
-class ScreenshotAnimationController(private val view: ScreenshotShelfView) {
+class ScreenshotAnimationController(
+    private val view: ScreenshotShelfView,
+    private val viewModel: ScreenshotViewModel
+) {
     private var animator: Animator? = null
     private val screenshotPreview = view.requireViewById<ImageView>(R.id.screenshot_preview)
+    private val scrollingScrim = view.requireViewById<ImageView>((R.id.screenshot_scrolling_scrim))
+    private val scrollTransitionPreview =
+        view.requireViewById<ImageView>(R.id.screenshot_scrollable_preview)
     private val flashView = view.requireViewById<View>(R.id.screenshot_flash)
     private val actionContainer = view.requireViewById<View>(R.id.actions_container_background)
     private val fastOutSlowIn =
@@ -46,6 +58,14 @@
             view.requireViewById(R.id.screenshot_badge),
             view.requireViewById(R.id.screenshot_dismiss_button)
         )
+    private val fadeUI =
+        listOf<View>(
+            view.requireViewById(R.id.screenshot_preview_border),
+            view.requireViewById(R.id.actions_container_background),
+            view.requireViewById(R.id.screenshot_badge),
+            view.requireViewById(R.id.screenshot_dismiss_button),
+            view.requireViewById(R.id.screenshot_message_container),
+        )
 
     fun getEntranceAnimation(
         bounds: Rect,
@@ -96,15 +116,108 @@
         }
         entranceAnimation.play(fadeInAnimator).after(previewAnimator)
         entranceAnimation.doOnStart {
+            viewModel.setIsAnimating(true)
             for (child in staticUI) {
                 child.alpha = 0f
             }
         }
+        entranceAnimation.doOnEnd { viewModel.setIsAnimating(false) }
 
         this.animator = entranceAnimation
         return entranceAnimation
     }
 
+    fun fadeForSharedTransition() {
+        animator?.cancel()
+        val fadeAnimator = ValueAnimator.ofFloat(1f, 0f)
+        fadeAnimator.addUpdateListener {
+            for (view in fadeUI) {
+                view.alpha = it.animatedValue as Float
+            }
+        }
+        animator = fadeAnimator
+        fadeAnimator.start()
+    }
+
+    fun runLongScreenshotTransition(
+        destRect: Rect,
+        longScreenshot: ScrollCaptureController.LongScreenshot,
+        onTransitionEnd: Runnable
+    ): Animator {
+        val animSet = AnimatorSet()
+
+        val scrimAnim = ValueAnimator.ofFloat(0f, 1f)
+        scrimAnim.addUpdateListener { animation: ValueAnimator ->
+            scrollingScrim.setAlpha(1 - animation.animatedFraction)
+        }
+        scrollTransitionPreview.visibility = View.VISIBLE
+        if (true) {
+            scrollTransitionPreview.setImageBitmap(longScreenshot.toBitmap())
+            val startX: Float = scrollTransitionPreview.x
+            val startY: Float = scrollTransitionPreview.y
+            val locInScreen: IntArray = scrollTransitionPreview.getLocationOnScreen()
+            destRect.offset(startX.toInt() - locInScreen[0], startY.toInt() - locInScreen[1])
+            scrollTransitionPreview.pivotX = 0f
+            scrollTransitionPreview.pivotY = 0f
+            scrollTransitionPreview.setAlpha(1f)
+            val currentScale: Float = scrollTransitionPreview.width / longScreenshot.width.toFloat()
+            val matrix = Matrix()
+            matrix.setScale(currentScale, currentScale)
+            matrix.postTranslate(
+                longScreenshot.left * currentScale,
+                longScreenshot.top * currentScale
+            )
+            scrollTransitionPreview.setImageMatrix(matrix)
+            val destinationScale: Float = destRect.width() / scrollTransitionPreview.width.toFloat()
+            val previewAnim = ValueAnimator.ofFloat(0f, 1f)
+            previewAnim.addUpdateListener { animation: ValueAnimator ->
+                val t = animation.animatedFraction
+                val currScale = MathUtils.lerp(1f, destinationScale, t)
+                scrollTransitionPreview.scaleX = currScale
+                scrollTransitionPreview.scaleY = currScale
+                scrollTransitionPreview.x = MathUtils.lerp(startX, destRect.left.toFloat(), t)
+                scrollTransitionPreview.y = MathUtils.lerp(startY, destRect.top.toFloat(), t)
+            }
+            val previewFadeAnim = ValueAnimator.ofFloat(1f, 0f)
+            previewFadeAnim.addUpdateListener { animation: ValueAnimator ->
+                scrollTransitionPreview.setAlpha(1 - animation.animatedFraction)
+            }
+            previewAnim.doOnEnd { onTransitionEnd.run() }
+            animSet.play(previewAnim).with(scrimAnim).before(previewFadeAnim)
+        } else {
+            // if we switched orientations between the original screenshot and the long screenshot
+            // capture, just fade out the scrim instead of running the preview animation
+            scrimAnim.doOnEnd { onTransitionEnd.run() }
+            animSet.play(scrimAnim)
+        }
+        animator = animSet
+        return animSet
+    }
+
+    fun fadeForLongScreenshotTransition() {
+        scrollingScrim.imageTintBlendMode = BlendMode.SRC_ATOP
+        val anim = ValueAnimator.ofFloat(0f, .3f)
+        anim.addUpdateListener {
+            scrollingScrim.setImageTintList(
+                ColorStateList.valueOf(Color.argb(it.animatedValue as Float, 0f, 0f, 0f))
+            )
+        }
+        for (view in fadeUI) {
+            view.alpha = 0f
+        }
+        screenshotPreview.alpha = 0f
+        anim.setDuration(200)
+        anim.start()
+    }
+
+    fun restoreUI() {
+        animator?.cancel()
+        for (view in fadeUI) {
+            view.alpha = 1f
+        }
+        screenshotPreview.alpha = 1f
+    }
+
     fun getSwipeReturnAnimation(): Animator {
         animator?.cancel()
         val animator = ValueAnimator.ofFloat(view.translationX, 0f)
@@ -114,6 +227,7 @@
     }
 
     fun getSwipeDismissAnimation(requestedVelocity: Float?): Animator {
+        animator?.cancel()
         val velocity = getAdjustedVelocity(requestedVelocity)
         val screenWidth = view.resources.displayMetrics.widthPixels
         // translation at which point the visible UI is fully off the screen (in the direction
@@ -131,6 +245,8 @@
             view.alpha = 1f - it.animatedFraction
         }
         animator.duration = ((abs(distance / velocity))).toLong()
+        animator.doOnStart { viewModel.setIsAnimating(true) }
+        animator.doOnEnd { viewModel.setIsAnimating(false) }
 
         this.animator = animator
         return animator
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
index 916d50f..969cf48 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
@@ -22,6 +22,8 @@
 import android.graphics.Rect
 import android.graphics.Region
 import android.util.AttributeSet
+import android.view.GestureDetector
+import android.view.GestureDetector.SimpleOnGestureListener
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewGroup
@@ -30,7 +32,6 @@
 import android.widget.ImageView
 import com.android.systemui.res.R
 import com.android.systemui.screenshot.FloatingWindowUtil
-import kotlin.math.max
 
 class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) :
     FrameLayout(context, attrs) {
@@ -39,11 +40,50 @@
     private lateinit var screenshotStatic: ViewGroup
     var onTouchInterceptListener: ((MotionEvent) -> Boolean)? = null
 
+    var userInteractionCallback: (() -> Unit)? = null
+
     private val displayMetrics = context.resources.displayMetrics
     private val tmpRect = Rect()
     private lateinit var actionsContainerBackground: View
+    private lateinit var actionsContainer: View
     private lateinit var dismissButton: View
 
+    // Prepare an internal `GestureDetector` to determine when we can initiate a touch-interception
+    // session (with the client's provided `onTouchInterceptListener`). We delegate out to their
+    // listener only for gestures that can't be handled by scrolling our `actionsContainer`.
+    private val gestureDetector =
+        GestureDetector(
+            context,
+            object : SimpleOnGestureListener() {
+                override fun onScroll(
+                    ev1: MotionEvent?,
+                    ev2: MotionEvent,
+                    distanceX: Float,
+                    distanceY: Float
+                ): Boolean {
+                    actionsContainer.getBoundsOnScreen(tmpRect)
+                    val touchedInActionsContainer =
+                        tmpRect.contains(ev2.rawX.toInt(), ev2.rawY.toInt())
+                    val canHandleInternallyByScrolling =
+                        touchedInActionsContainer
+                        && actionsContainer.canScrollHorizontally(distanceX.toInt())
+                    return !canHandleInternallyByScrolling
+                }
+            }
+        )
+
+    init {
+
+        // Delegate to the client-provided `onTouchInterceptListener` if we've already initiated
+        // touch-interception.
+        setOnTouchListener({ _: View, ev: MotionEvent ->
+            userInteractionCallback?.invoke()
+            onTouchInterceptListener?.invoke(ev) ?: false
+        })
+
+        gestureDetector.setIsLongpressEnabled(false)
+    }
+
     override fun onFinishInflate() {
         super.onFinishInflate()
         // Get focus so that the key events go to the layout.
@@ -52,7 +92,15 @@
         blurredScreenshotPreview = requireViewById(R.id.screenshot_preview_blur)
         screenshotStatic = requireViewById(R.id.screenshot_static)
         actionsContainerBackground = requireViewById(R.id.actions_container_background)
+        actionsContainer = requireViewById(R.id.actions_container)
         dismissButton = requireViewById(R.id.screenshot_dismiss_button)
+
+        // Configure to extend the timeout during ongoing gestures (i.e. scrolls) that are already
+        // being handled by our child views.
+        actionsContainer.setOnTouchListener({ _: View, ev: MotionEvent ->
+            userInteractionCallback?.invoke()
+            false
+        })
     }
 
     fun getTouchRegion(gestureInsets: Insets): Region {
@@ -79,6 +127,18 @@
         val inPortrait = orientation == Configuration.ORIENTATION_PORTRAIT
         val cutout = insets.displayCutout
         val navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars())
+
+        // When honoring the navbar or other obstacle offsets, include some extra padding above
+        // the inset itself.
+        val verticalPadding =
+            mContext.resources.getDimensionPixelOffset(R.dimen.screenshot_shelf_vertical_margin)
+
+        // Minimum bottom padding to always enforce (e.g. if there's no nav bar)
+        val minimumBottomPadding =
+            context.resources.getDimensionPixelOffset(
+                R.dimen.overlay_action_container_minimum_edge_spacing
+            )
+
         if (cutout == null) {
             screenshotStatic.setPadding(0, 0, 0, navBarInsets.bottom)
         } else {
@@ -86,25 +146,41 @@
             if (inPortrait) {
                 screenshotStatic.setPadding(
                     waterfall.left,
-                    max(cutout.safeInsetTop.toDouble(), waterfall.top.toDouble()).toInt(),
+                    max(cutout.safeInsetTop, waterfall.top),
                     waterfall.right,
                     max(
-                            cutout.safeInsetBottom.toDouble(),
-                            max(navBarInsets.bottom.toDouble(), waterfall.bottom.toDouble())
-                        )
-                        .toInt()
+                        navBarInsets.bottom + verticalPadding,
+                        cutout.safeInsetBottom + verticalPadding,
+                        waterfall.bottom + verticalPadding,
+                        minimumBottomPadding,
+                    )
                 )
             } else {
                 screenshotStatic.setPadding(
-                    max(cutout.safeInsetLeft.toDouble(), waterfall.left.toDouble()).toInt(),
+                    max(cutout.safeInsetLeft, waterfall.left),
                     waterfall.top,
-                    max(cutout.safeInsetRight.toDouble(), waterfall.right.toDouble()).toInt(),
-                    max(navBarInsets.bottom.toDouble(), waterfall.bottom.toDouble()).toInt()
+                    max(cutout.safeInsetRight, waterfall.right),
+                    max(
+                        navBarInsets.bottom + verticalPadding,
+                        waterfall.bottom + verticalPadding,
+                        minimumBottomPadding,
+                    )
                 )
             }
         }
     }
 
+    // Max function for two or more params.
+    private fun max(first: Int, second: Int, vararg items: Int): Int {
+        var largest = if (first > second) first else second
+        for (item in items) {
+            if (item > largest) {
+                largest = item
+            }
+        }
+        return largest
+    }
+
     private fun getSwipeRegion(): Region {
         val swipeRegion = Region()
         val padding = FloatingWindowUtil.dpToPx(displayMetrics, -1 * TOUCH_PADDING_DP).toInt()
@@ -127,10 +203,24 @@
         private const val TOUCH_PADDING_DP = 12f
     }
 
+    override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
+        userInteractionCallback?.invoke()
+        return super.onInterceptHoverEvent(event)
+    }
+
     override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
-        if (onTouchInterceptListener?.invoke(ev) == true) {
-            return true
+        userInteractionCallback?.invoke()
+
+        // Let the client-provided listener see all `DOWN` events so that they'll be able to
+        // interpret the remainder of the gesture, even if interception starts partway-through.
+        // TODO: is this really necessary? And if we don't go on to start interception, should we
+        // follow up with `ACTION_CANCEL`?
+        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            onTouchInterceptListener?.invoke(ev)
         }
-        return super.onInterceptTouchEvent(ev)
+
+        // Only allow the client-provided touch interceptor to take over the gesture if our
+        // top-level `GestureDetector` decides not to scroll the action container.
+        return gestureDetector.onTouchEvent(ev)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt
index 750bd53..36aa39f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt
@@ -23,8 +23,9 @@
 import com.android.systemui.res.R
 import com.android.systemui.screenshot.ui.TransitioningIconDrawable
 import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel
+import javax.inject.Inject
 
-object ActionButtonViewBinder {
+class ActionButtonViewBinder @Inject constructor() {
     /** Binds the given view to the given view-model */
     fun bind(view: View, viewModel: ActionButtonViewModel) {
         val iconView = view.requireViewById<ImageView>(R.id.overlay_action_chip_icon)
@@ -36,7 +37,19 @@
         // Note we never re-bind a view to a different ActionButtonViewModel, different view
         // models would remove/create separate views.
         drawable?.setIcon(viewModel.appearance.icon)
+        iconView.setImageDrawable(viewModel.appearance.icon)
+        if (!viewModel.appearance.tint) {
+            iconView.setImageTintList(null)
+        }
         textView.text = viewModel.appearance.label
+
+        viewModel.appearance.customBackground?.also {
+            if (it.canApplyTheme()) {
+                it.applyTheme(view.rootView.context.theme)
+            }
+            view.background = it
+        }
+
         setMargins(iconView, textView, viewModel.appearance.label?.isNotEmpty() ?: false)
         if (viewModel.onClicked != null) {
             view.setOnClickListener { viewModel.onClicked.invoke() }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
index 43c0107..442b387 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
@@ -16,7 +16,11 @@
 
 package com.android.systemui.screenshot.ui.binder
 
+import android.content.res.Configuration
 import android.graphics.Bitmap
+import android.graphics.Matrix
+import android.graphics.Rect
+import android.util.LayoutDirection
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
@@ -29,22 +33,27 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.screenshot.ScreenshotEvent
+import com.android.systemui.screenshot.ui.ScreenshotAnimationController
 import com.android.systemui.screenshot.ui.ScreenshotShelfView
 import com.android.systemui.screenshot.ui.SwipeGestureListener
 import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel
 import com.android.systemui.screenshot.ui.viewmodel.AnimationState
 import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
 import com.android.systemui.util.children
+import javax.inject.Inject
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 
-object ScreenshotShelfViewBinder {
+class ScreenshotShelfViewBinder
+@Inject
+constructor(private val buttonViewBinder: ActionButtonViewBinder) {
     fun bind(
         view: ScreenshotShelfView,
         viewModel: ScreenshotViewModel,
+        animationController: ScreenshotAnimationController,
         layoutInflater: LayoutInflater,
         onDismissalRequested: (event: ScreenshotEvent, velocity: Float?) -> Unit,
-        onDismissalCancelled: () -> Unit,
+        onUserInteraction: () -> Unit
     ) {
         val swipeGestureListener =
             SwipeGestureListener(
@@ -52,20 +61,25 @@
                 onDismiss = {
                     onDismissalRequested(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, it)
                 },
-                onCancel = onDismissalCancelled
+                onCancel = { animationController.getSwipeReturnAnimation().start() }
             )
         view.onTouchInterceptListener = { swipeGestureListener.onMotionEvent(it) }
+        view.userInteractionCallback = onUserInteraction
 
         val previewView: ImageView = view.requireViewById(R.id.screenshot_preview)
         val previewViewBlur: ImageView = view.requireViewById(R.id.screenshot_preview_blur)
         val previewBorder = view.requireViewById<View>(R.id.screenshot_preview_border)
         previewView.clipToOutline = true
         previewViewBlur.clipToOutline = true
+        val actionsContainer: LinearLayout = view.requireViewById(R.id.screenshot_actions)
         val dismissButton = view.requireViewById<View>(R.id.screenshot_dismiss_button)
         dismissButton.visibility = if (viewModel.showDismissButton) View.VISIBLE else View.GONE
         dismissButton.setOnClickListener {
             onDismissalRequested(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL, null)
         }
+        val scrollingScrim: ImageView = view.requireViewById(R.id.screenshot_scrolling_scrim)
+        val scrollablePreview: ImageView = view.requireViewById(R.id.screenshot_scrollable_preview)
+        val badgeView = view.requireViewById<ImageView>(R.id.screenshot_badge)
 
         // use immediate dispatcher to ensure screenshot bitmap is set before animation
         view.repeatWhenAttached(Dispatchers.Main.immediate) {
@@ -85,11 +99,48 @@
                         }
                     }
                     launch {
+                        viewModel.scrollingScrim.collect { bitmap ->
+                            if (bitmap != null) {
+                                scrollingScrim.setImageBitmap(bitmap)
+                                scrollingScrim.visibility = View.VISIBLE
+                            } else {
+                                scrollingScrim.visibility = View.GONE
+                            }
+                        }
+                    }
+                    launch {
+                        viewModel.scrollableRect.collect { rect ->
+                            if (rect != null) {
+                                setScrollablePreview(
+                                    scrollablePreview,
+                                    viewModel.preview.value,
+                                    rect
+                                )
+                            } else {
+                                scrollablePreview.visibility = View.GONE
+                            }
+                        }
+                    }
+                    launch {
+                        viewModel.badge.collect { badge ->
+                            badgeView.setImageDrawable(badge)
+                            badgeView.visibility = if (badge != null) View.VISIBLE else View.GONE
+                        }
+                    }
+                    launch {
                         viewModel.previewAction.collect { onClick ->
                             previewView.setOnClickListener { onClick?.invoke() }
                         }
                     }
                     launch {
+                        viewModel.isAnimating.collect { isAnimating ->
+                            previewView.isClickable = !isAnimating
+                            for (child in actionsContainer.children) {
+                                child.isClickable = !isAnimating
+                            }
+                        }
+                    }
+                    launch {
                         viewModel.actions.collect { actions ->
                             updateActions(
                                 actions,
@@ -149,14 +200,14 @@
             val currentView: View? = actionsContainer.getChildAt(index)
             if (action.id == currentView?.tag) {
                 // Same ID, update the display
-                ActionButtonViewBinder.bind(currentView, action)
+                buttonViewBinder.bind(currentView, action)
             } else {
                 // Different ID. Removals have already happened so this must
                 // mean that the new action must be inserted here.
                 val actionButton =
                     layoutInflater.inflate(R.layout.shelf_action_chip, actionsContainer, false)
                 actionsContainer.addView(actionButton, index)
-                ActionButtonViewBinder.bind(actionButton, action)
+                buttonViewBinder.bind(actionButton, action)
             }
         }
     }
@@ -179,4 +230,35 @@
         screenshotPreview.layoutParams = params
         screenshotPreview.requestLayout()
     }
+
+    private fun setScrollablePreview(
+        scrollablePreview: ImageView,
+        bitmap: Bitmap?,
+        scrollableRect: Rect
+    ) {
+        if (bitmap == null) {
+            return
+        }
+        val fixedSize = scrollablePreview.resources.getDimensionPixelSize(R.dimen.overlay_x_scale)
+        val inPortrait =
+            scrollablePreview.resources.configuration.orientation ==
+                Configuration.ORIENTATION_PORTRAIT
+        val scale: Float = fixedSize / ((if (inPortrait) bitmap.width else bitmap.height).toFloat())
+        val params = scrollablePreview.layoutParams
+
+        params.width = (scale * scrollableRect.width()).toInt()
+        params.height = (scale * scrollableRect.height()).toInt()
+        val matrix = Matrix()
+        matrix.setScale(scale, scale)
+        matrix.postTranslate(-scrollableRect.left * scale, -scrollableRect.top * scale)
+
+        scrollablePreview.translationX =
+            (scale *
+                if (scrollablePreview.layoutDirection == LayoutDirection.LTR) scrollableRect.left
+                else scrollableRect.right - (scrollablePreview.parent as View).width)
+        scrollablePreview.translationY = scale * scrollableRect.top
+        scrollablePreview.setImageMatrix(matrix)
+        scrollablePreview.setImageBitmap(bitmap)
+        scrollablePreview.setVisibility(View.VISIBLE)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt
index 55a2ad2..42ad326 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt
@@ -19,8 +19,12 @@
 import android.graphics.drawable.Drawable
 
 /** Data describing how an action should be shown to the user. */
-data class ActionButtonAppearance(
+data class ActionButtonAppearance
+@JvmOverloads
+constructor(
     val icon: Drawable?,
     val label: CharSequence?,
     val description: CharSequence,
+    val tint: Boolean = true,
+    val customBackground: Drawable? = null,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
index 5f36f73..3f99bc4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.screenshot.ui.viewmodel
 
 import android.graphics.Bitmap
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
 import android.util.Log
 import android.view.accessibility.AccessibilityManager
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -25,6 +27,10 @@
 class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager) {
     private val _preview = MutableStateFlow<Bitmap?>(null)
     val preview: StateFlow<Bitmap?> = _preview
+    private val _scrollingScrim = MutableStateFlow<Bitmap?>(null)
+    val scrollingScrim: StateFlow<Bitmap?> = _scrollingScrim
+    private val _badge = MutableStateFlow<Drawable?>(null)
+    val badge: StateFlow<Drawable?> = _badge
     private val _previewAction = MutableStateFlow<(() -> Unit)?>(null)
     val previewAction: StateFlow<(() -> Unit)?> = _previewAction
     private val _actions = MutableStateFlow(emptyList<ActionButtonViewModel>())
@@ -32,6 +38,10 @@
     private val _animationState = MutableStateFlow(AnimationState.NOT_STARTED)
     val animationState: StateFlow<AnimationState> = _animationState
 
+    private val _isAnimating = MutableStateFlow(false)
+    val isAnimating: StateFlow<Boolean> = _isAnimating
+    private val _scrollableRect = MutableStateFlow<Rect?>(null)
+    val scrollableRect: StateFlow<Rect?> = _scrollableRect
     val showDismissButton: Boolean
         get() = accessibilityManager.isEnabled
 
@@ -39,6 +49,14 @@
         _preview.value = bitmap
     }
 
+    fun setScrollingScrimBitmap(bitmap: Bitmap?) {
+        _scrollingScrim.value = bitmap
+    }
+
+    fun setScreenshotBadge(badge: Drawable?) {
+        _badge.value = badge
+    }
+
     fun setPreviewAction(onClick: () -> Unit) {
         _previewAction.value = onClick
     }
@@ -107,11 +125,23 @@
         _animationState.value = state
     }
 
+    fun setIsAnimating(isAnimating: Boolean) {
+        _isAnimating.value = isAnimating
+    }
+
+    fun setScrollableRect(rect: Rect?) {
+        _scrollableRect.value = rect
+    }
+
     fun reset() {
         _preview.value = null
+        _scrollingScrim.value = null
+        _badge.value = null
         _previewAction.value = null
         _actions.value = listOf()
         _animationState.value = AnimationState.NOT_STARTED
+        _isAnimating.value = false
+        _scrollableRect.value = null
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
index 288ff09..84156eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
@@ -51,6 +51,16 @@
         return super.onTouchEvent(event);
     }
 
+    @Override
+    public boolean onHoverEvent(MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
+            setHovered(true);
+        } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
+            setHovered(false);
+        }
+        return true;
+    }
+
     public void setAccessibilityLabel(String label) {
         mAccessibilityLabel = label;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 6367d44b..22aa492 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -40,6 +40,7 @@
 import com.android.systemui.communal.dagger.Communal
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.ui.compose.CommunalContainer
+import com.android.systemui.communal.ui.compose.CommunalContent
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.util.CommunalColors
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -50,7 +51,6 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
@@ -69,12 +69,12 @@
 constructor(
     private val communalInteractor: CommunalInteractor,
     private val communalViewModel: CommunalViewModel,
-    private val dialogFactory: SystemUIDialogFactory,
     private val keyguardInteractor: KeyguardInteractor,
     private val shadeInteractor: ShadeInteractor,
     private val powerManager: PowerManager,
     private val communalColors: CommunalColors,
     private val ambientTouchComponentFactory: AmbientTouchComponent.Factory,
+    private val communalContent: CommunalContent,
     @Communal private val dataSourceDelegator: SceneDataSourceDelegator
 ) : LifecycleOwner {
     /** The container view for the hub. This will not be initialized until [initView] is called. */
@@ -123,15 +123,9 @@
     private var anyBouncerShowing = false
 
     /**
-     * True if the shade is fully expanded and the user is not interacting with it anymore, meaning
-     * the hub should not receive any touch input.
+     * True if the shade is fully expanded, meaning the hub should not receive any touch input.
      *
-     * We need to not pause the touch handling lifecycle as soon as the shade opens because if the
-     * user swipes down, then back up without lifting their finger, the lifecycle will be paused
-     * then resumed, and resuming force-stops all active touch sessions. This means the shade will
-     * not receive the end of the gesture and will be stuck open.
-     *
-     * Based on [ShadeInteractor.isAnyFullyExpanded] and [ShadeInteractor.isUserInteracting].
+     * Tracks [ShadeInteractor.isAnyFullyExpanded].
      */
     private var shadeShowing = false
 
@@ -180,7 +174,7 @@
                                         viewModel = communalViewModel,
                                         colors = communalColors,
                                         dataSourceDelegator = dataSourceDelegator,
-                                        dialogFactory = dialogFactory,
+                                        content = communalContent,
                                     )
                                 }
                             }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7051d5f..32e383a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -141,6 +141,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
 import com.android.systemui.keyguard.shared.ComposeLockscreen;
+import com.android.systemui.keyguard.shared.model.Edge;
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
@@ -170,6 +171,7 @@
 import com.android.systemui.power.shared.model.WakefulnessModel;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
 import com.android.systemui.shade.data.repository.FlingInfo;
 import com.android.systemui.shade.data.repository.ShadeRepository;
 import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
@@ -251,6 +253,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Consumer;
 
 import javax.inject.Inject;
@@ -447,6 +450,9 @@
     private final ShadeHeadsUpTrackerImpl mShadeHeadsUpTracker = new ShadeHeadsUpTrackerImpl();
     private final ShadeFoldAnimatorImpl mShadeFoldAnimator = new ShadeFoldAnimatorImpl();
 
+    @VisibleForTesting
+    Set<Animator> mTestSetOfAnimatorsUsed;
+
     private boolean mShowIconsWhenExpanded;
     private int mIndicationBottomPadding;
     private int mAmbientIndicationBottomPadding;
@@ -1130,8 +1136,12 @@
                 controller.setup(mNotificationContainerParent));
 
         // Dreaming->Lockscreen
-        collectFlow(mView, mKeyguardTransitionInteractor.transition(DREAMING, LOCKSCREEN),
-                mDreamingToLockscreenTransition, mMainDispatcher);
+        collectFlow(
+                mView,
+                mKeyguardTransitionInteractor.transition(
+                        Edge.Companion.create(DREAMING, LOCKSCREEN)),
+                mDreamingToLockscreenTransition,
+                mMainDispatcher);
         collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(),
                 setDreamLockscreenTransitionAlpha(mNotificationStackScrollLayoutController),
                 mMainDispatcher);
@@ -1141,7 +1151,8 @@
 
         // Gone -> Dreaming hosted in lockscreen
         collectFlow(mView, mKeyguardTransitionInteractor
-                        .transition(GONE, DREAMING_LOCKSCREEN_HOSTED),
+                        .transition(Edge.Companion.create(Scenes.Gone, DREAMING_LOCKSCREEN_HOSTED),
+                                Edge.Companion.create(GONE, DREAMING_LOCKSCREEN_HOSTED)),
                 mGoneToDreamingLockscreenHostedTransition, mMainDispatcher);
         collectFlow(mView, mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha(),
                 setTransitionAlpha(mNotificationStackScrollLayoutController),
@@ -1149,16 +1160,17 @@
 
         // Lockscreen -> Dreaming hosted in lockscreen
         collectFlow(mView, mKeyguardTransitionInteractor
-                        .transition(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED),
+                        .transition(Edge.Companion.create(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)),
                 mLockscreenToDreamingLockscreenHostedTransition, mMainDispatcher);
 
         // Dreaming hosted in lockscreen -> Lockscreen
         collectFlow(mView, mKeyguardTransitionInteractor
-                        .transition(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN),
+                        .transition(Edge.Companion.create(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)),
                 mDreamingLockscreenHostedToLockscreenTransition, mMainDispatcher);
 
         // Occluded->Lockscreen
-        collectFlow(mView, mKeyguardTransitionInteractor.transition(OCCLUDED, LOCKSCREEN),
+        collectFlow(mView, mKeyguardTransitionInteractor.transition(
+                Edge.Companion.create(OCCLUDED, LOCKSCREEN)),
                 mOccludedToLockscreenTransition, mMainDispatcher);
         if (!MigrateClocksToBlueprint.isEnabled()) {
             collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
@@ -1169,7 +1181,8 @@
         }
 
         // Lockscreen->Dreaming
-        collectFlow(mView, mKeyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING),
+        collectFlow(mView, mKeyguardTransitionInteractor.transition(
+                Edge.Companion.create(LOCKSCREEN, DREAMING)),
                 mLockscreenToDreamingTransition, mMainDispatcher);
         if (!MigrateClocksToBlueprint.isEnabled()) {
             collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(),
@@ -1181,7 +1194,9 @@
                 setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
 
         // Gone->Dreaming
-        collectFlow(mView, mKeyguardTransitionInteractor.transition(GONE, DREAMING),
+        collectFlow(mView, mKeyguardTransitionInteractor.transition(
+                Edge.Companion.create(Scenes.Gone, DREAMING),
+                        Edge.Companion.create(GONE, DREAMING)),
                 mGoneToDreamingTransition, mMainDispatcher);
         if (!MigrateClocksToBlueprint.isEnabled()) {
             collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(),
@@ -1192,7 +1207,8 @@
                 setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
 
         // Lockscreen->Occluded
-        collectFlow(mView, mKeyguardTransitionInteractor.transition(LOCKSCREEN, OCCLUDED),
+        collectFlow(mView, mKeyguardTransitionInteractor.transition(
+                Edge.Companion.create(LOCKSCREEN, OCCLUDED)),
                 mLockscreenToOccludedTransition, mMainDispatcher);
         if (!MigrateClocksToBlueprint.isEnabled()) {
             collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(),
@@ -4137,6 +4153,8 @@
     }
 
     private void setAnimator(ValueAnimator animator) {
+        // TODO(b/341163515): Should we clean up the old animator?
+        registerAnimatorForTest(animator);
         mHeightAnimator = animator;
         if (animator == null && mPanelUpdateWhenAnimatorEnds) {
             mPanelUpdateWhenAnimatorEnds = false;
@@ -4181,6 +4199,7 @@
     private ValueAnimator createHeightAnimator(float targetHeight, float overshootAmount) {
         float startExpansion = mOverExpansion;
         ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
+        registerAnimatorForTest(animator);
         animator.addUpdateListener(
                 animation -> {
                     if (overshootAmount > 0.0f
@@ -4198,6 +4217,12 @@
         return animator;
     }
 
+    private void registerAnimatorForTest(Animator animator) {
+        if (mTestSetOfAnimatorsUsed != null) {
+            mTestSetOfAnimatorsUsed.add(animator);
+        }
+    }
+
     /** Update the visibility of {@link NotificationPanelView} if necessary. */
     private void updateVisibility() {
         mView.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index b50a3cd..6efa633 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -49,6 +49,7 @@
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.MigrateClocksToBlueprint;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.Edge;
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.res.R;
@@ -137,11 +138,6 @@
     private final PanelExpansionInteractor mPanelExpansionInteractor;
     private final ShadeExpansionStateManager mShadeExpansionStateManager;
 
-    /**
-     * If {@code true}, an external touch sent in {@link #handleExternalTouch(MotionEvent)} has been
-     * intercepted and all future touch events for the gesture should be processed by this view.
-     */
-    private boolean mExternalTouchIntercepted = false;
     private boolean mIsTrackingBarGesture = false;
     private boolean mIsOcclusionTransitionRunning = false;
     private DisableSubpixelTextTransitionListener mDisableSubpixelTextTransitionListener;
@@ -225,7 +221,8 @@
         mDisableSubpixelTextTransitionListener = new DisableSubpixelTextTransitionListener(mView);
         bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container));
 
-        collectFlow(mView, keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING),
+        collectFlow(mView, keyguardTransitionInteractor.transition(
+                Edge.Companion.create(LOCKSCREEN, DREAMING)),
                 mLockscreenToDreamingTransition);
         collectFlow(
                 mView,
@@ -258,28 +255,11 @@
     }
 
     /**
-     * Handle a touch event while dreaming or on the hub by forwarding the event to the content
-     * view.
-     * <p>
-     * Since important logic for handling touches lives in the dispatch/intercept phases, we
-     * simulate going through all of these stages before sending onTouchEvent if intercepted.
-     *
+     * Handle a touch event while dreaming by forwarding the event to the content view.
      * @param event The event to forward.
      */
-    public void handleExternalTouch(MotionEvent event) {
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mExternalTouchIntercepted = false;
-        }
-
-        if (!mView.dispatchTouchEvent(event)) {
-            return;
-        }
-        if (!mExternalTouchIntercepted) {
-            mExternalTouchIntercepted = mView.onInterceptTouchEvent(event);
-        }
-        if (mExternalTouchIntercepted) {
-            mView.onTouchEvent(event);
-        }
+    public void handleDreamTouch(MotionEvent event) {
+        mView.dispatchTouchEvent(event);
     }
 
     /** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index d2c93da..884ccef 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -140,7 +140,7 @@
 
     private fun animateCollapseShadeInternal() {
         sceneInteractor.changeScene(
-            getCollapseDestinationScene(),
+            getCollapseDestinationScene(), // TODO(b/336581871): add sceneState?
             "ShadeController.animateCollapseShade",
             SlightlyFasterShadeCollapse,
         )
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
index c9949cd..55bd8c6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
@@ -44,6 +44,7 @@
                 } else {
                     Scenes.Shade
                 }
+            // TODO(b/336581871): add sceneState?
             sceneInteractor.changeScene(key, "animateCollapseQs")
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index f509ef5..e4a2424 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -33,7 +33,7 @@
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.GoneToSplitShade
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
@@ -150,7 +150,7 @@
                 else -> Scenes.Lockscreen
             }
 
-        val upTransitionKey = GoneToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+        val upTransitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
 
         val down = Scenes.QuickSettings.takeIf { shadeMode is ShadeMode.Single }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index d955349..c912616 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -134,7 +134,7 @@
     private static final int MSG_DISMISS_KEYBOARD_SHORTCUTS        = 32 << MSG_SHIFT;
     private static final int MSG_HANDLE_SYSTEM_KEY                 = 33 << MSG_SHIFT;
     private static final int MSG_SHOW_GLOBAL_ACTIONS               = 34 << MSG_SHIFT;
-    private static final int MSG_TOGGLE_PANEL                      = 35 << MSG_SHIFT;
+    private static final int MSG_TOGGLE_NOTIFICATION_PANEL         = 35 << MSG_SHIFT;
     private static final int MSG_SHOW_SHUTDOWN_UI                  = 36 << MSG_SHIFT;
     private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR      = 37 << MSG_SHIFT;
     private static final int MSG_ROTATION_PROPOSAL                 = 38 << MSG_SHIFT;
@@ -180,6 +180,7 @@
     private static final int MSG_SET_QS_TILES = 79 << MSG_SHIFT;
     private static final int MSG_ENTER_DESKTOP = 80 << MSG_SHIFT;
     private static final int MSG_SET_SPLITSCREEN_FOCUS = 81 << MSG_SHIFT;
+    private static final int MSG_TOGGLE_QUICK_SETTINGS_PANEL = 82 << MSG_SHIFT;
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
     public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
@@ -222,10 +223,33 @@
          */
         default void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2,
                 boolean animate) { }
+
+        /**
+         * Called to expand Notifications panel with animation.
+         */
         default void animateExpandNotificationsPanel() { }
+        /**
+         * Called to collapse Notifications panel with animation.
+         * @param flags Exclusion flags. See {@link FLAG_EXCLUDE_NONE}.
+         * @param force True to force the operation.
+         */
         default void animateCollapsePanels(int flags, boolean force) { }
-        default void togglePanel() { }
-        default void animateExpandSettingsPanel(String obj) { }
+
+        /**
+         * Called to toggle Notifications panel.
+         */
+        default void toggleNotificationsPanel() { }
+
+        /**
+         * Called to expand Quick Settings panel with animation.
+         * @param subPanel subPanel one wish to expand.
+         */
+        default void animateExpandSettingsPanel(String subPanel) { }
+
+        /**
+         * Called to toggle Quick Settings panel.
+         */
+        default void toggleQuickSettingsPanel() { }
 
         /**
          * Called to notify IME window status changes.
@@ -696,10 +720,10 @@
         }
     }
 
-    public void togglePanel() {
+    public void toggleNotificationsPanel() {
         synchronized (mLock) {
-            mHandler.removeMessages(MSG_TOGGLE_PANEL);
-            mHandler.obtainMessage(MSG_TOGGLE_PANEL, 0, 0).sendToTarget();
+            mHandler.removeMessages(MSG_TOGGLE_NOTIFICATION_PANEL);
+            mHandler.obtainMessage(MSG_TOGGLE_NOTIFICATION_PANEL, 0, 0).sendToTarget();
         }
     }
 
@@ -710,6 +734,13 @@
         }
     }
 
+    public void toggleQuickSettingsPanel() {
+        synchronized (mLock) {
+            mHandler.removeMessages(MSG_TOGGLE_QUICK_SETTINGS_PANEL);
+            mHandler.obtainMessage(MSG_TOGGLE_QUICK_SETTINGS_PANEL, 0, 0).sendToTarget();
+        }
+    }
+
     @Override
     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
@@ -1494,9 +1525,9 @@
                         mCallbacks.get(i).animateCollapsePanels(msg.arg1, msg.arg2 != 0);
                     }
                     break;
-                case MSG_TOGGLE_PANEL:
+                case MSG_TOGGLE_NOTIFICATION_PANEL:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).togglePanel();
+                        mCallbacks.get(i).toggleNotificationsPanel();
                     }
                     break;
                 case MSG_EXPAND_SETTINGS:
@@ -1504,6 +1535,11 @@
                         mCallbacks.get(i).animateExpandSettingsPanel((String) msg.obj);
                     }
                     break;
+                case MSG_TOGGLE_QUICK_SETTINGS_PANEL:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).toggleQuickSettingsPanel();
+                    }
+                    break;
                 case MSG_SHOW_IME_BUTTON:
                     args = (SomeArgs) msg.obj;
                     handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index d7d3732..5bf2f41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.Flags.mediaControlsUserInitiatedDismiss;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
@@ -175,14 +177,18 @@
             }
 
             @Override
-            public void onMediaDataRemoved(@NonNull String key) {
+            public void onMediaDataRemoved(@NonNull String key, boolean userInitiated) {
+                if (mediaControlsUserInitiatedDismiss() && !userInitiated) {
+                    // Dismissing the notification will send the app's deleteIntent, so ignore if
+                    // this was an automatic removal
+                    Log.d(TAG, "Not dismissing " + key + " because it was removed by the system");
+                    return;
+                }
                 mNotifPipeline.getAllNotifs()
                         .stream()
                         .filter(entry -> Objects.equals(entry.getKey(), key))
                         .findAny()
                         .ifPresent(entry -> {
-                            // TODO(b/160713608): "removing" this notification won't happen and
-                            //  won't send the 'deleteIntent' if the notification is ongoing.
                             mNotifCollection.dismissNotification(entry,
                                     getDismissedByUserStats(entry));
                         });
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainer.kt
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainer.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainer.kt
index ce88a5f..cae86a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainer.kt
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone.ongoingcall
+package com.android.systemui.statusbar.chips.ui.view
 
 import android.content.Context
 import android.util.AttributeSet
 import com.android.systemui.animation.view.LaunchableLinearLayout
 
 /**
- * A container view for the ongoing call chip background. Needed so that we can limit the height of
- * the background when the font size is very large (200%), in which case the background would go
+ * A container view for the ongoing activity chip background. Needed so that we can limit the height
+ * of the background when the font size is very large (200%), in which case the background would go
  * past the bounds of the status bar.
  */
-class OngoingCallBackgroundContainer(context: Context, attrs: AttributeSet) :
+class ChipBackgroundContainer(context: Context, attrs: AttributeSet) :
     LaunchableLinearLayout(context, attrs) {
 
     /** Sets where this view should fetch its max height from. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometer.kt
similarity index 72%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometer.kt
index bb7ba4c..ff3061e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometer.kt
@@ -14,36 +14,34 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone.ongoingcall
+package com.android.systemui.statusbar.chips.ui.view
 
 import android.content.Context
 import android.util.AttributeSet
-
 import android.widget.Chronometer
 import androidx.annotation.UiThread
 
 /**
- * A [Chronometer] specifically for the ongoing call chip in the status bar.
+ * A [Chronometer] specifically for chips in the status bar that show ongoing duration of an
+ * activity.
  *
  * This class handles:
- *   1) Setting the text width. If we used a basic WRAP_CONTENT for width, the chip width would
- *      change slightly each second because the width of each number is slightly different.
+ * 1) Setting the text width. If we used a basic WRAP_CONTENT for width, the chip width would change
+ *    slightly each second because the width of each number is slightly different.
  *
- *      Instead, we save the largest number width seen so far and ensure that the chip is at least
- *      that wide. This means the chip may get larger over time (e.g. in the transition from 59:59
- *      to 1:00:00), but never smaller.
- *
- *   2) Hiding the text if the time gets too long for the space available. Once the text has been
- *      hidden, it remains hidden for the duration of the call.
+ *    Instead, we save the largest number width seen so far and ensure that the chip is at least
+ *    that wide. This means the chip may get larger over time (e.g. in the transition from 59:59 to
+ *    1:00:00), but never smaller.
+ * 2) Hiding the text if the time gets too long for the space available. Once the text has been
+ *    hidden, it remains hidden for the duration of the activity.
  *
  * Note that if the text was too big in portrait mode, resulting in the text being hidden, then the
  * text will also be hidden in landscape (even if there is enough space for it in landscape).
  */
-class OngoingCallChronometer @JvmOverloads constructor(
-    context: Context,
-    attrs: AttributeSet? = null,
-    defStyle: Int = 0
-) : Chronometer(context, attrs, defStyle) {
+class ChipChronometer
+@JvmOverloads
+constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) :
+    Chronometer(context, attrs, defStyle) {
 
     // Minimum width that the text view can be. Corresponds with the largest number width seen so
     // far.
@@ -53,8 +51,8 @@
     private var shouldHideText: Boolean = false
 
     override fun setBase(base: Long) {
-        // These variables may have changed during the previous call, so re-set them before the new
-        // call starts.
+        // These variables may have changed during the previous activity, so re-set them before the
+        // new activity starts.
         minimumTextWidth = 0
         shouldHideText = false
         visibility = VISIBLE
@@ -75,9 +73,7 @@
         }
 
         // Evaluate how wide the text *wants* to be if it had unlimited space.
-        super.onMeasure(
-                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
-                heightMeasureSpec)
+        super.onMeasure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightMeasureSpec)
         val desiredTextWidth = measuredWidth
 
         // Evaluate how wide the text *can* be based on the enforced constraints
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index 3d0fd89..af2c197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -31,8 +31,10 @@
 import com.android.systemui.statusbar.notification.icon.ConversationIconManager
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
 import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
+import com.android.systemui.statusbar.notification.stack.BUCKET_PRIORITY_PEOPLE
 import javax.inject.Inject
 
 /**
@@ -81,6 +83,13 @@
         }
     }
 
+    val priorityPeopleSectioner =
+            object : NotifSectioner("Priority People", BUCKET_PRIORITY_PEOPLE) {
+                override fun isInSection(entry: ListEntry): Boolean {
+                    return getPeopleType(entry) == TYPE_IMPORTANT_PERSON
+                }
+            }
+
     // TODO(b/330193582): Rename to just "People"
     val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) {
         override fun isInSection(entry: ListEntry): Boolean  {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 1a223c1..42bf4e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -18,12 +18,10 @@
 
 package com.android.systemui.statusbar.notification.collection.coordinator
 
-import android.os.SystemProperties
 import android.os.UserHandle
 import android.provider.Settings
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.Dumpable
-import com.android.systemui.Flags.notificationMinimalismPrototype
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
@@ -33,14 +31,20 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.expansionChanges
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.headsUpEvents
 import com.android.systemui.util.asIndenting
@@ -107,6 +111,10 @@
     }
 
     private fun attachUnseenFilter(pipeline: NotifPipeline) {
+        if (NotificationMinimalismPrototype.V2.isEnabled) {
+            pipeline.addPromoter(unseenNotifPromoter)
+            pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotif)
+        }
         pipeline.addFinalizeFilter(unseenNotifFilter)
         pipeline.addCollectionListener(collectionListener)
         scope.launch { trackUnseenFilterSettingChanges() }
@@ -264,7 +272,10 @@
     }
 
     private fun unseenFeatureEnabled(): Flow<Boolean> {
-        if (notificationMinimalismPrototype()) {
+        if (
+            NotificationMinimalismPrototype.V1.isEnabled ||
+                NotificationMinimalismPrototype.V2.isEnabled
+        ) {
             return flowOf(true)
         }
         return secureSettings
@@ -335,6 +346,57 @@
             }
         }
 
+    private fun pickOutTopUnseenNotif(list: List<ListEntry>) {
+        if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return
+        // Only ever elevate a top unseen notification on keyguard, not even locked shade
+        if (statusBarStateController.state != StatusBarState.KEYGUARD) {
+            seenNotificationsInteractor.setTopUnseenNotification(null)
+            return
+        }
+        // On keyguard pick the top-ranked unseen or ongoing notification to elevate
+        seenNotificationsInteractor.setTopUnseenNotification(
+            list
+                .asSequence()
+                .flatMap {
+                    when (it) {
+                        is NotificationEntry -> listOfNotNull(it)
+                        is GroupEntry -> it.children
+                        else -> error("unhandled type of $it")
+                    }
+                }
+                .filter { shouldIgnoreUnseenCheck(it) || it in unseenNotifications }
+                .minByOrNull { it.ranking.rank }
+        )
+    }
+
+    @VisibleForTesting
+    internal val unseenNotifPromoter =
+        object : NotifPromoter("$TAG-unseen") {
+            override fun shouldPromoteToTopLevel(child: NotificationEntry): Boolean =
+                if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false
+                else
+                    seenNotificationsInteractor.isTopUnseenNotification(child) &&
+                        NotificationMinimalismPrototype.V2.ungroupTopUnseen
+        }
+
+    val unseenNotifSectioner =
+        object : NotifSectioner("Unseen", BUCKET_FOREGROUND_SERVICE) {
+            override fun isInSection(entry: ListEntry): Boolean {
+                if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return false
+                if (
+                    seenNotificationsInteractor.isTopUnseenNotification(entry.representativeEntry)
+                ) {
+                    return true
+                }
+                if (entry !is GroupEntry) {
+                    return false
+                }
+                return entry.children.any {
+                    seenNotificationsInteractor.isTopUnseenNotification(it)
+                }
+            }
+        }
+
     @VisibleForTesting
     internal val unseenNotifFilter =
         object : NotifFilter("$TAG-unseen") {
@@ -342,18 +404,6 @@
             var hasFilteredAnyNotifs = false
 
             /**
-             * the [notificationMinimalismPrototype] will now show seen notifications on the locked
-             * shade by default, but this property read allows that to be quickly disabled for
-             * testing
-             */
-            private val minimalismShowOnLockedShade
-                get() =
-                    SystemProperties.getBoolean(
-                        "persist.notification_minimalism_prototype.show_on_locked_shade",
-                        true
-                    )
-
-            /**
              * Encapsulates a definition of "being on the keyguard". Note that these two definitions
              * are wildly different: [StatusBarState.KEYGUARD] is when on the lock screen and does
              * not include shade or occluded states, whereas [KeyguardRepository.isKeyguardShowing]
@@ -364,7 +414,12 @@
              * allow seen notifications to appear in the locked shade.
              */
             private fun isOnKeyguard(): Boolean =
-                if (notificationMinimalismPrototype() && minimalismShowOnLockedShade) {
+                if (NotificationMinimalismPrototype.V2.isEnabled) {
+                    false // disable this feature under this prototype
+                } else if (
+                    NotificationMinimalismPrototype.V1.isEnabled &&
+                        NotificationMinimalismPrototype.V1.showOnLockedShade
+                ) {
                     statusBarStateController.state == StatusBarState.KEYGUARD
                 } else {
                     keyguardRepository.isKeyguardShowing()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 36c12a7..4506385 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -24,47 +24,51 @@
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
 import javax.inject.Inject
 
 /**
- * Handles the attachment of [Coordinator]s to the [NotifPipeline] so that the
- * Coordinators can register their respective callbacks.
+ * Handles the attachment of [Coordinator]s to the [NotifPipeline] so that the Coordinators can
+ * register their respective callbacks.
  */
 interface NotifCoordinators : Coordinator, PipelineDumpable
 
 @CoordinatorScope
-class NotifCoordinatorsImpl @Inject constructor(
-        sectionStyleProvider: SectionStyleProvider,
-        featureFlags: FeatureFlags,
-        dataStoreCoordinator: DataStoreCoordinator,
-        hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
-        hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
-        keyguardCoordinator: KeyguardCoordinator,
-        rankingCoordinator: RankingCoordinator,
-        colorizedFgsCoordinator: ColorizedFgsCoordinator,
-        deviceProvisionedCoordinator: DeviceProvisionedCoordinator,
-        bubbleCoordinator: BubbleCoordinator,
-        headsUpCoordinator: HeadsUpCoordinator,
-        gutsCoordinator: GutsCoordinator,
-        conversationCoordinator: ConversationCoordinator,
-        debugModeCoordinator: DebugModeCoordinator,
-        groupCountCoordinator: GroupCountCoordinator,
-        groupWhenCoordinator: GroupWhenCoordinator,
-        mediaCoordinator: MediaCoordinator,
-        preparationCoordinator: PreparationCoordinator,
-        remoteInputCoordinator: RemoteInputCoordinator,
-        rowAlertTimeCoordinator: RowAlertTimeCoordinator,
-        rowAppearanceCoordinator: RowAppearanceCoordinator,
-        stackCoordinator: StackCoordinator,
-        shadeEventCoordinator: ShadeEventCoordinator,
-        smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
-        viewConfigCoordinator: ViewConfigCoordinator,
-        visualStabilityCoordinator: VisualStabilityCoordinator,
-        sensitiveContentCoordinator: SensitiveContentCoordinator,
-        dismissibilityCoordinator: DismissibilityCoordinator,
-        dreamCoordinator: DreamCoordinator,
-        statsLoggerCoordinator: NotificationStatsLoggerCoordinator,
+class NotifCoordinatorsImpl
+@Inject
+constructor(
+    sectionStyleProvider: SectionStyleProvider,
+    featureFlags: FeatureFlags,
+    dataStoreCoordinator: DataStoreCoordinator,
+    hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
+    hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
+    keyguardCoordinator: KeyguardCoordinator,
+    rankingCoordinator: RankingCoordinator,
+    colorizedFgsCoordinator: ColorizedFgsCoordinator,
+    deviceProvisionedCoordinator: DeviceProvisionedCoordinator,
+    bubbleCoordinator: BubbleCoordinator,
+    headsUpCoordinator: HeadsUpCoordinator,
+    gutsCoordinator: GutsCoordinator,
+    conversationCoordinator: ConversationCoordinator,
+    debugModeCoordinator: DebugModeCoordinator,
+    groupCountCoordinator: GroupCountCoordinator,
+    groupWhenCoordinator: GroupWhenCoordinator,
+    mediaCoordinator: MediaCoordinator,
+    preparationCoordinator: PreparationCoordinator,
+    remoteInputCoordinator: RemoteInputCoordinator,
+    rowAlertTimeCoordinator: RowAlertTimeCoordinator,
+    rowAppearanceCoordinator: RowAppearanceCoordinator,
+    stackCoordinator: StackCoordinator,
+    shadeEventCoordinator: ShadeEventCoordinator,
+    smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
+    viewConfigCoordinator: ViewConfigCoordinator,
+    visualStabilityCoordinator: VisualStabilityCoordinator,
+    sensitiveContentCoordinator: SensitiveContentCoordinator,
+    dismissibilityCoordinator: DismissibilityCoordinator,
+    dreamCoordinator: DreamCoordinator,
+    statsLoggerCoordinator: NotificationStatsLoggerCoordinator,
 ) : NotifCoordinators {
 
     private val mCoreCoordinators: MutableList<CoreCoordinator> = ArrayList()
@@ -114,6 +118,12 @@
         // Manually add Ordered Sections
         mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
         mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService
+        if (NotificationMinimalismPrototype.V2.isEnabled) {
+            mOrderedSections.add(keyguardCoordinator.unseenNotifSectioner) // Unseen (FGS)
+        }
+        if (PriorityPeopleSection.isEnabled) {
+            mOrderedSections.add(conversationCoordinator.priorityPeopleSectioner) // Priority People
+        }
         mOrderedSections.add(conversationCoordinator.peopleAlertingSectioner) // People Alerting
         if (!SortBySectionTimeFlag.isEnabled) {
             mOrderedSections.add(conversationCoordinator.peopleSilentSectioner) // People Silent
@@ -124,22 +134,26 @@
 
         sectionStyleProvider.setMinimizedSections(setOf(rankingCoordinator.minimizedSectioner))
         if (SortBySectionTimeFlag.isEnabled) {
-            sectionStyleProvider.setSilentSections(listOf(
+            sectionStyleProvider.setSilentSections(
+                listOf(
                     rankingCoordinator.silentSectioner,
                     rankingCoordinator.minimizedSectioner,
-            ))
+                )
+            )
         } else {
-            sectionStyleProvider.setSilentSections(listOf(
+            sectionStyleProvider.setSilentSections(
+                listOf(
                     conversationCoordinator.peopleSilentSectioner,
                     rankingCoordinator.silentSectioner,
                     rankingCoordinator.minimizedSectioner,
-            ))
+                )
+            )
         }
     }
 
     /**
-     * Sends the pipeline to each coordinator when the pipeline is ready to accept
-     * [Pluggable]s, [NotifCollectionListener]s and [NotifLifetimeExtender]s.
+     * Sends the pipeline to each coordinator when the pipeline is ready to accept [Pluggable]s,
+     * [NotifCollectionListener]s and [NotifLifetimeExtender]s.
      */
     override fun attach(pipeline: NotifPipeline) {
         for (c in mCoreCoordinators) {
@@ -155,10 +169,11 @@
      * As part of the NotifPipeline dumpable, dumps the list of coordinators; sections are omitted
      * as they are dumped in the RenderStageManager instead.
      */
-    override fun dumpPipeline(d: PipelineDumper) = with(d) {
-        dump("core coordinators", mCoreCoordinators)
-        dump("normal coordinators", mCoordinators)
-    }
+    override fun dumpPipeline(d: PipelineDumper) =
+        with(d) {
+            dump("core coordinators", mCoreCoordinators)
+            dump("normal coordinators", mCoordinators)
+        }
 
     companion object {
         private const val TAG = "NotifCoordinators"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 350e88e..caa6c17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -38,6 +38,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -63,6 +65,7 @@
     public static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
     private final DelayableExecutor mDelayableExecutor;
     private final HeadsUpManager mHeadsUpManager;
+    private final SeenNotificationsInteractor mSeenNotificationsInteractor;
     private final ShadeAnimationInteractor mShadeAnimationInteractor;
     private final StatusBarStateController mStatusBarStateController;
     private final JavaAdapter mJavaAdapter;
@@ -101,6 +104,7 @@
             HeadsUpManager headsUpManager,
             ShadeAnimationInteractor shadeAnimationInteractor,
             JavaAdapter javaAdapter,
+            SeenNotificationsInteractor seenNotificationsInteractor,
             StatusBarStateController statusBarStateController,
             VisibilityLocationProvider visibilityLocationProvider,
             VisualStabilityProvider visualStabilityProvider,
@@ -109,6 +113,7 @@
         mHeadsUpManager = headsUpManager;
         mShadeAnimationInteractor = shadeAnimationInteractor;
         mJavaAdapter = javaAdapter;
+        mSeenNotificationsInteractor = seenNotificationsInteractor;
         mVisibilityLocationProvider = visibilityLocationProvider;
         mVisualStabilityProvider = visualStabilityProvider;
         mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -142,8 +147,15 @@
     private final NotifStabilityManager mNotifStabilityManager =
             new NotifStabilityManager("VisualStabilityCoordinator") {
                 private boolean canMoveForHeadsUp(NotificationEntry entry) {
-                    return entry != null && mHeadsUpManager.isHeadsUpEntry(entry.getKey())
-                            && !mVisibilityLocationProvider.isInVisibleLocation(entry);
+                    if (entry == null) {
+                        return false;
+                    }
+                    boolean isTopUnseen = NotificationMinimalismPrototype.V2.isEnabled()
+                            && mSeenNotificationsInteractor.isTopUnseenNotification(entry);
+                    if (isTopUnseen || mHeadsUpManager.isHeadsUpEntry(entry.getKey())) {
+                        return !mVisibilityLocationProvider.isInVisibleLocation(entry);
+                    }
+                    return false;
                 }
 
                 @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
index 5c844bc..e2c9e02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
@@ -41,6 +41,9 @@
 
     /** Stats about the list of notifications attached to the shade */
     val notifStats = MutableStateFlow(NotifStats.empty)
+
+    /** The key of the top unseen notification */
+    val topUnseenNotificationKey = MutableStateFlow<String?>(null)
 }
 
 /** Represents the notification list, comprised of groups and individual notifications. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
index 1f7ab96..42828d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
@@ -17,7 +17,9 @@
 package com.android.systemui.statusbar.notification.domain.interactor
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
 import javax.inject.Inject
 import kotlinx.coroutines.flow.StateFlow
 
@@ -36,4 +38,15 @@
     fun setHasFilteredOutSeenNotifications(value: Boolean) {
         notificationListRepository.hasFilteredOutSeenNotifications.value = value
     }
+
+    /** Set the entry that is identified as the top unseen notification. */
+    fun setTopUnseenNotification(entry: NotificationEntry?) {
+        if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return
+        notificationListRepository.topUnseenNotificationKey.value = entry?.key
+    }
+
+    /** Determine if the given notification is the top unseen notification. */
+    fun isTopUnseenNotification(entry: NotificationEntry?): Boolean =
+        if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false
+        else entry != null && notificationListRepository.topUnseenNotificationKey.value == entry.key
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 968b591..5a616df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -18,7 +18,7 @@
 
 import static android.graphics.PorterDuff.Mode.SRC_ATOP;
 
-import static com.android.systemui.Flags.notificationBackgroundTintOptimization;
+import static com.android.systemui.Flags.notificationFooterBackgroundTintOptimization;
 import static com.android.systemui.util.ColorUtilKt.hexColorString;
 
 import android.annotation.ColorInt;
@@ -407,7 +407,7 @@
         final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
         final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
         final @ColorInt int scHigh;
-        if (!notificationBackgroundTintOptimization()) {
+        if (!notificationFooterBackgroundTintOptimization()) {
             scHigh = Utils.getColorAttrDefaultColor(mContext,
                     com.android.internal.R.attr.materialColorSurfaceContainerHigh);
             if (scHigh != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index bfc5932..a901c5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -241,9 +241,6 @@
     ) {
     val TAG = "AvalancheSuppressor"
 
-    override var reason: String = "avalanche"
-        protected set
-
     enum class State {
         ALLOW_CONVERSATION_AFTER_AVALANCHE,
         ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME,
@@ -257,19 +254,15 @@
 
     override fun shouldSuppress(entry: NotificationEntry): Boolean {
         if (!isCooldownEnabled()) {
-            reason = "FALSE avalanche cooldown setting DISABLED"
             return false
         }
         val timeSinceAvalancheMs = systemClock.currentTimeMillis() - avalancheProvider.startTime
         val timedOut = timeSinceAvalancheMs >= avalancheProvider.timeoutMs
         if (timedOut) {
-            reason = "FALSE avalanche event TIMED OUT. " +
-                    "${timeSinceAvalancheMs/1000} seconds since last avalanche"
             return false
         }
         val state = calculateState(entry)
         if (state != State.SUPPRESS) {
-            reason = "FALSE avalanche IN ALLOWLIST: $state"
             return false
         }
         return true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
index 89aa3ab..9e0dd8fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
@@ -21,6 +21,7 @@
 import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_HEADS_UP;
 import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_MEDIA_CONTROLS;
 import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PEOPLE;
+import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PRIORITY_PEOPLE;
 import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
 
 import android.annotation.Nullable;
@@ -130,7 +131,8 @@
             case BUCKET_HEADS_UP: return Notifications.Notification.SECTION_HEADS_UP;
             case BUCKET_FOREGROUND_SERVICE:
                 return Notifications.Notification.SECTION_FOREGROUND_SERVICE;
-            case BUCKET_PEOPLE: return Notifications.Notification.SECTION_PEOPLE;
+            case BUCKET_PEOPLE, BUCKET_PRIORITY_PEOPLE:
+                return Notifications.Notification.SECTION_PEOPLE;
             case BUCKET_ALERTING: return Notifications.Notification.SECTION_ALERTING;
             case BUCKET_SILENT: return Notifications.Notification.SECTION_SILENT;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 747cb3c..edd2961 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -101,6 +101,7 @@
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationCompactMessagingTemplateViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -590,7 +591,9 @@
             mMenuRow.setAppName(mAppName);
         }
         if (mIsSummaryWithChildren) {
-            if (!AsyncGroupHeaderViewInflation.isEnabled()) {
+            if (AsyncGroupHeaderViewInflation.isEnabled()) {
+                mChildrenContainer.updateGroupHeaderExpandState();
+            } else {
                 // We create the header from the background thread instead
                 mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
                         isConversation());
@@ -843,6 +846,16 @@
     }
 
     /**
+     *
+     * @return true when compact version of Heads Up is on the screen.
+     */
+    public boolean isCompactConversationHeadsUpOnScreen() {
+        final NotificationViewWrapper viewWrapper =
+                getVisibleNotificationViewWrapper();
+
+        return viewWrapper instanceof NotificationCompactMessagingTemplateViewWrapper;
+    }
+    /**
      * @see NotificationChildrenContainer#setUntruncatedChildCount(int)
      */
     public void setUntruncatedChildCount(int childCount) {
@@ -2788,7 +2801,10 @@
         }
     }
 
-    protected void expandNotification() {
+    /**
+     * Triggers expand click listener to expand the notification.
+     */
+    public void expandNotification() {
         mExpandClickListener.onClick(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
index db3cf5a..9d0fcd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.row
 
 import android.app.Flags
+import android.os.SystemProperties
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
 import javax.inject.Inject
 
@@ -34,8 +35,13 @@
     HeadsUpStyleProvider {
 
     override fun shouldApplyCompactStyle(): Boolean {
-        // Use compact HUN for immersive mode.
-        return Flags.compactHeadsUpNotification() &&
-            statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode.value
+        return Flags.compactHeadsUpNotification() && (isInImmersiveMode() || alwaysShow())
     }
+
+    private fun isInImmersiveMode() =
+        statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode.value
+
+    /** developer setting to always show Minimal HUN, even if the device is not in full screen */
+    private fun alwaysShow() =
+        SystemProperties.getBoolean("persist.compact_heads_up_notification.always_show", false)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt
index ce87d2f..3a5f3b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt
@@ -24,7 +24,7 @@
 /**
  * Compact Heads up Notifications template that doesn't set feedback icon and audibly alert icons
  */
-class NotificationCompactHeadsUpTemplateViewWrapper(
+open class NotificationCompactHeadsUpTemplateViewWrapper(
     ctx: Context,
     view: View,
     row: ExpandableNotificationRow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt
new file mode 100644
index 0000000..bb40b56
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import com.android.internal.R
+import com.android.internal.widget.CachingIconView
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+
+/** Wraps a notification containing a messaging or conversation template */
+class NotificationCompactMessagingTemplateViewWrapper
+constructor(ctx: Context, view: View, row: ExpandableNotificationRow) :
+    NotificationCompactHeadsUpTemplateViewWrapper(ctx, view, row) {
+
+    private val compactMessagingView: ViewGroup = requireNotNull(view as? ViewGroup)
+
+    private var conversationIconView: CachingIconView? = null
+    private var expandBtn: View? = null
+    override fun onContentUpdated(row: ExpandableNotificationRow?) {
+        resolveViews()
+        super.onContentUpdated(row)
+    }
+
+    private fun resolveViews() {
+        conversationIconView = compactMessagingView.requireViewById(R.id.conversation_icon)
+        expandBtn = compactMessagingView.requireViewById(R.id.expand_button)
+    }
+
+    override fun updateTransformedTypes() {
+        super.updateTransformedTypes()
+
+        addViewsTransformingToSimilar(
+            conversationIconView,
+            expandBtn,
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 4244542..22b95ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -74,6 +74,8 @@
                 return new NotificationCallTemplateViewWrapper(ctx, v, row);
             } else if ("compactHUN".equals((v.getTag()))) {
                 return new NotificationCompactHeadsUpTemplateViewWrapper(ctx, v, row);
+            } else if ("compactMessagingHUN".equals((v.getTag()))) {
+                return new NotificationCompactMessagingTemplateViewWrapper(ctx, v, row);
             }
 
             if (row.getEntry().getSbn().getNotification().isStyle(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt
index d4f8ea3..d6c73a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt
@@ -23,8 +23,8 @@
 /** Helper for reading or using the heads-up cycling flag state. */
 @Suppress("NOTHING_TO_INLINE")
 object NotificationHeadsUpCycling {
-    /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_NOTIFICATION_HEADS_UP_CYCLING
+    /** The aconfig flag name - enable this feature when FLAG_NOTIFICATION_THROTTLE_HUN is on. */
+    const val FLAG_NAME = Flags.FLAG_NOTIFICATION_THROTTLE_HUN
 
     /** A token used for dependency declaration */
     val token: FlagToken
@@ -33,7 +33,7 @@
     /** Is the heads-up cycling animation enabled */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.notificationHeadsUpCycling()
+        get() = Flags.notificationThrottleHun()
 
     /** Whether to animate the bottom line when transiting from a tall HUN to a short HUN */
     @JvmStatic
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt
new file mode 100644
index 0000000..bf37036
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.shared
+
+import android.os.SystemProperties
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the minimalism prototype flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationMinimalismPrototype {
+
+    val version: Int by lazy {
+        SystemProperties.getInt("persist.notification_minimalism_prototype.version", 2)
+    }
+
+    object V1 {
+        /** The aconfig flag name */
+        const val FLAG_NAME = Flags.FLAG_NOTIFICATION_MINIMALISM_PROTOTYPE
+
+        /** A token used for dependency declaration */
+        val token: FlagToken
+            get() = FlagToken(FLAG_NAME, isEnabled)
+
+        /** Is the heads-up cycling animation enabled */
+        @JvmStatic
+        inline val isEnabled
+            get() = Flags.notificationMinimalismPrototype() && version == 1
+
+        /**
+         * the prototype will now show seen notifications on the locked shade by default, but this
+         * property read allows that to be quickly disabled for testing
+         */
+        val showOnLockedShade: Boolean
+            get() =
+                if (isUnexpectedlyInLegacyMode()) false
+                else
+                    SystemProperties.getBoolean(
+                        "persist.notification_minimalism_prototype.show_on_locked_shade",
+                        true
+                    )
+
+        /** gets the configurable max number of notifications */
+        val maxNotifs: Int
+            get() =
+                if (isUnexpectedlyInLegacyMode()) -1
+                else
+                    SystemProperties.getInt(
+                        "persist.notification_minimalism_prototype.lock_screen_max_notifs",
+                        1
+                    )
+
+        /**
+         * Called to ensure code is only run when the flag is enabled. This protects users from the
+         * unintended behaviors caused by accidentally running new logic, while also crashing on an
+         * eng build to ensure that the refactor author catches issues in testing.
+         */
+        @JvmStatic
+        inline fun isUnexpectedlyInLegacyMode() =
+            RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+        /**
+         * Called to ensure code is only run when the flag is disabled. This will throw an exception
+         * if the flag is enabled to ensure that the refactor author catches issues in testing.
+         */
+        @JvmStatic
+        inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+    }
+    object V2 {
+        const val FLAG_NAME = Flags.FLAG_NOTIFICATION_MINIMALISM_PROTOTYPE
+
+        /** A token used for dependency declaration */
+        val token: FlagToken
+            get() = FlagToken(FLAG_NAME, isEnabled)
+
+        /** Is the heads-up cycling animation enabled */
+        @JvmStatic
+        inline val isEnabled
+            get() = Flags.notificationMinimalismPrototype() && version == 2
+
+        /**
+         * The prototype will (by default) use a promoter to ensure that the top unseen notification
+         * is not grouped, but this property read allows that behavior to be disabled.
+         */
+        val ungroupTopUnseen: Boolean
+            get() =
+                if (isUnexpectedlyInLegacyMode()) false
+                else
+                    SystemProperties.getBoolean(
+                        "persist.notification_minimalism_prototype.ungroup_top_unseen",
+                        true
+                    )
+
+        /**
+         * Called to ensure code is only run when the flag is enabled. This protects users from the
+         * unintended behaviors caused by accidentally running new logic, while also crashing on an
+         * eng build to ensure that the refactor author catches issues in testing.
+         */
+        @JvmStatic
+        inline fun isUnexpectedlyInLegacyMode() =
+            RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+        /**
+         * Called to ensure code is only run when the flag is disabled. This will throw an exception
+         * if the flag is enabled to ensure that the refactor author catches issues in testing.
+         */
+        @JvmStatic
+        inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/PriorityPeopleSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/PriorityPeopleSection.kt
new file mode 100644
index 0000000..472fd95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/PriorityPeopleSection.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for com.android.systemui.Flags.FLAG_PRIORITY_PEOPLE_SECTION */
+@Suppress("NOTHING_TO_INLINE")
+object PriorityPeopleSection {
+    const val FLAG_NAME = Flags.FLAG_PRIORITY_PEOPLE_SECTION
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Are sections sorted by time? */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.priorityPeopleSection()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 92c597c..48796d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -439,6 +439,15 @@
         Trace.endSection();
     }
 
+    /**
+     * Update the expand state of the group header.
+     */
+    public void updateGroupHeaderExpandState() {
+        if (mGroupHeaderWrapper != null) {
+            mGroupHeaderWrapper.setExpanded(mChildrenExpanded);
+        }
+    }
+
     private void removeGroupHeader() {
         if (mGroupHeader == null) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
index 31f4857..fc28a99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
@@ -3,15 +3,22 @@
 import android.annotation.IntDef
 
 /**
- * For now, declare the available notification buckets (sections) here so that other
- * presentation code can decide what to do based on an entry's buckets
+ * For now, declare the available notification buckets (sections) here so that other presentation
+ * code can decide what to do based on an entry's buckets
  */
 @Retention(AnnotationRetention.SOURCE)
 @IntDef(
-        prefix = ["BUCKET_"],
-        value = [
-            BUCKET_UNKNOWN, BUCKET_MEDIA_CONTROLS, BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE,
-            BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT
+    prefix = ["BUCKET_"],
+    value =
+        [
+            BUCKET_UNKNOWN,
+            BUCKET_MEDIA_CONTROLS,
+            BUCKET_HEADS_UP,
+            BUCKET_FOREGROUND_SERVICE,
+            BUCKET_PRIORITY_PEOPLE,
+            BUCKET_PEOPLE,
+            BUCKET_ALERTING,
+            BUCKET_SILENT
         ]
 )
 annotation class PriorityBucket
@@ -20,6 +27,7 @@
 const val BUCKET_MEDIA_CONTROLS = 1
 const val BUCKET_HEADS_UP = 2
 const val BUCKET_FOREGROUND_SERVICE = 3
+const val BUCKET_PRIORITY_PEOPLE = 7
 const val BUCKET_PEOPLE = 4
 const val BUCKET_ALERTING = 5
 const val BUCKET_SILENT = 6
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 1ef9c6c..17b54c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -674,7 +674,7 @@
     void setOverExpansion(float margin) {
         mAmbientState.setOverExpansion(margin);
         if (notificationOverExpansionClippingFix() && !SceneContainerFlag.isEnabled()) {
-            setRoundingClippingYTranslation((int) margin);
+            setRoundingClippingYTranslation(mShouldUseSplitNotificationShade ? (int) margin : 0);
         }
         updateStackPosition();
         requestChildrenUpdate();
@@ -3433,15 +3433,19 @@
             int action = ev.getActionMasked();
             boolean isUpOrCancel = action == ACTION_UP || action == ACTION_CANCEL;
             if (mSendingTouchesToSceneFramework) {
-                mController.sendTouchToSceneFramework(ev);
+                MotionEvent adjustedEvent = MotionEvent.obtain(ev);
+                adjustedEvent.setLocation(ev.getRawX(), ev.getRawY());
+                mController.sendTouchToSceneFramework(adjustedEvent);
                 mScrollViewFields.sendCurrentGestureOverscroll(
                         getExpandedInThisMotion() && !isUpOrCancel);
+                adjustedEvent.recycle();
             } else if (!isUpOrCancel) {
                 // if this is the first touch being sent to the scene framework,
                 // convert it into a synthetic DOWN event.
                 mSendingTouchesToSceneFramework = true;
                 MotionEvent downEvent = MotionEvent.obtain(ev);
                 downEvent.setAction(MotionEvent.ACTION_DOWN);
+                downEvent.setLocation(ev.getRawX(), ev.getRawY());
                 mController.sendTouchToSceneFramework(downEvent);
                 mScrollViewFields.sendCurrentGestureOverscroll(getExpandedInThisMotion());
                 downEvent.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 5bd4c75..4b0b1e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -17,11 +17,9 @@
 package com.android.systemui.statusbar.notification.stack
 
 import android.content.res.Resources
-import android.os.SystemProperties
 import android.util.Log
 import android.view.View.GONE
 import androidx.annotation.VisibleForTesting
-import com.android.systemui.Flags.notificationMinimalismPrototype
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -31,6 +29,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.Compile
 import com.android.systemui.util.children
@@ -73,6 +72,9 @@
      */
     private var maxNotificationsExcludesMedia = false
 
+    /** Whether we allow keyguard to show less important notifications above the shelf. */
+    private var limitLockScreenToImportant = false
+
     /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */
     private var dividerHeight by notNull<Float>()
 
@@ -86,6 +88,14 @@
         updateResources()
     }
 
+    private fun allowedByPolicy(stackHeight: StackHeight): Boolean =
+        if (limitLockScreenToImportant && stackHeight.includesLessImportantNotification) {
+            log { "\tallowedByPolicy = false" }
+            false
+        } else {
+            true
+        }
+
     /**
      * Returns whether notifications and (shelf if visible) can fit in total space available.
      * [shelfSpace] is extra vertical space allowed for the shelf to overlap the lock icon.
@@ -184,11 +194,12 @@
         log { "\tGet maxNotifWithoutSavingSpace ---" }
         val maxNotifWithoutSavingSpace =
             stackHeightSequence.lastIndexWhile { heightResult ->
-                canStackFitInSpace(
-                    heightResult,
-                    notifSpace = notifSpace,
-                    shelfSpace = shelfSpace
-                ) == FitResult.FIT
+                allowedByPolicy(heightResult) &&
+                    canStackFitInSpace(
+                        heightResult,
+                        notifSpace = notifSpace,
+                        shelfSpace = shelfSpace
+                    ) == FitResult.FIT
             }
 
         // How many notifications we can show at heightWithoutLockscreenConstraints
@@ -213,11 +224,12 @@
             saveSpaceOnLockscreen = true
             maxNotifications =
                 stackHeightSequence.lastIndexWhile { heightResult ->
-                    canStackFitInSpace(
-                        heightResult,
-                        notifSpace = notifSpace,
-                        shelfSpace = shelfSpace
-                    ) != FitResult.NO_FIT
+                    allowedByPolicy(heightResult) &&
+                        canStackFitInSpace(
+                            heightResult,
+                            notifSpace = notifSpace,
+                            shelfSpace = shelfSpace
+                        ) != FitResult.NO_FIT
                 }
             log { "\t--- maxNotifications=$maxNotifications" }
         }
@@ -319,7 +331,10 @@
 
         // Float height of shelf (0 if shelf is not showing), and space before the shelf that
         // changes during the lockscreen <=> full shade transition.
-        val shelfHeightWithSpaceBefore: Float
+        val shelfHeightWithSpaceBefore: Float,
+
+        /** Whether this stack height includes less at least one important notification. */
+        val includesLessImportantNotification: Boolean
     )
 
     private fun computeHeightPerNotificationLimit(
@@ -332,12 +347,15 @@
         var previous: ExpandableView? = null
         val onLockscreen = onLockscreen()
 
+        var includesLessImportantNotification = false
+
         // Only shelf. This should never happen, since we allow 1 view minimum (EmptyViewState).
         yield(
             StackHeight(
                 notifsHeight = 0f,
                 notifsHeightSavingSpace = 0f,
-                shelfHeightWithSpaceBefore = shelfHeight
+                shelfHeightWithSpaceBefore = shelfHeight,
+                includesLessImportantNotification = includesLessImportantNotification,
             )
         )
 
@@ -363,6 +381,19 @@
                     spaceBeforeShelf + shelfHeight
                 }
 
+            if (limitLockScreenToImportant && !includesLessImportantNotification) {
+                val bucket = (currentNotification as? ExpandableNotificationRow)?.entry?.bucket
+                includesLessImportantNotification =
+                    when (bucket) {
+                        null,
+                        BUCKET_MEDIA_CONTROLS,
+                        BUCKET_HEADS_UP,
+                        BUCKET_FOREGROUND_SERVICE,
+                        BUCKET_PRIORITY_PEOPLE -> false
+                        else -> true
+                    }
+            }
+
             log {
                 "\tcomputeHeightPerNotificationLimit i=$i notifs=$notifications " +
                     "notifsHeightSavingSpace=$notifsWithCollapsedHun" +
@@ -372,7 +403,8 @@
                 StackHeight(
                     notifsHeight = notifications,
                     notifsHeightSavingSpace = notifsWithCollapsedHun,
-                    shelfHeightWithSpaceBefore = shelfWithSpaceBefore
+                    shelfHeightWithSpaceBefore = shelfWithSpaceBefore,
+                    includesLessImportantNotification = includesLessImportantNotification,
                 )
             )
         }
@@ -381,16 +413,18 @@
     fun updateResources() {
         maxKeyguardNotifications =
             infiniteIfNegative(
-                if (notificationMinimalismPrototype()) {
-                    SystemProperties.getInt(
-                        "persist.notification_minimalism_prototype.lock_screen_max_notifs",
-                        1
-                    )
+                if (NotificationMinimalismPrototype.V1.isEnabled) {
+                    NotificationMinimalismPrototype.V1.maxNotifs
+                } else if (NotificationMinimalismPrototype.V2.isEnabled) {
+                    1
                 } else {
                     resources.getInteger(R.integer.keyguard_max_notification_count)
                 }
             )
-        maxNotificationsExcludesMedia = notificationMinimalismPrototype()
+        maxNotificationsExcludesMedia =
+            NotificationMinimalismPrototype.V1.isEnabled ||
+                NotificationMinimalismPrototype.V2.isEnabled
+        limitLockScreenToImportant = NotificationMinimalismPrototype.V2.isEnabled
 
         dividerHeight =
             max(1f, resources.getDimensionPixelSize(R.dimen.notification_divider_height).toFloat())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
index dacafc4..db544ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
@@ -38,16 +38,6 @@
      */
     val shadeScrimBounds = MutableStateFlow<ShadeScrimBounds?>(null)
 
-    /**
-     * The y-coordinate in px of top of the contents of the notification stack. This value can be
-     * negative, if the stack is scrolled such that its top extends beyond the top edge of the
-     * screen.
-     */
-    val stackTop = MutableStateFlow(0f)
-
-    /** the bottom-most acceptable y-position for the bottom of the stack / shelf */
-    val stackBottom = MutableStateFlow(0f)
-
     /** the y position of the top of the HUN area */
     val headsUpTop = MutableStateFlow(0f)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 365ead6..e7acbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -72,12 +72,6 @@
     val alphaForBrightnessMirror: StateFlow<Float> =
         placeholderRepository.alphaForBrightnessMirror.asStateFlow()
 
-    /** The y-coordinate in px of top of the contents of the notification stack. */
-    val stackTop: StateFlow<Float> = placeholderRepository.stackTop.asStateFlow()
-
-    /** The y-coordinate in px of bottom of the contents of the notification stack. */
-    val stackBottom: StateFlow<Float> = placeholderRepository.stackBottom.asStateFlow()
-
     /** The height of the keyguard's available space bounds */
     val constrainedAvailableSpace: StateFlow<Int> =
         placeholderRepository.constrainedAvailableSpace.asStateFlow()
@@ -121,16 +115,6 @@
         viewHeightRepository.headsUpHeight.value = height
     }
 
-    /** Sets the y-coord in px of the top of the contents of the notification stack. */
-    fun setStackTop(stackTop: Float) {
-        placeholderRepository.stackTop.value = stackTop
-    }
-
-    /** Sets the y-coord in px of the bottom of the contents of the notification stack. */
-    fun setStackBottom(stackBottom: Float) {
-        placeholderRepository.stackBottom.value = stackBottom
-    }
-
     /** Sets whether the notification stack is scrolled to the top. */
     fun setScrolledToTop(scrolledToTop: Boolean) {
         placeholderRepository.scrolledToTop.value = scrolledToTop
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 3c44713..622d8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -79,8 +79,6 @@
         }
 
         launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
-        launch { viewModel.stackTop.collect { view.setStackTop(it) } }
-        launch { viewModel.stackBottom.collect { view.setStackBottom(it) } }
         launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
         launch { viewModel.headsUpTop.collect { view.setHeadsUpTop(it) } }
         launch { viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 082f6b6..6137381 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -130,10 +130,6 @@
     val maxAlpha: Flow<Float> =
         stackAppearanceInteractor.alphaForBrightnessMirror.dumpValue("maxAlpha")
 
-    /** The y-coordinate in px of top of the contents of the notification stack. */
-    val stackTop: Flow<Float> = stackAppearanceInteractor.stackTop.dumpValue("stackTop")
-    /** The y-coordinate in px of bottom of the contents of the notification stack. */
-    val stackBottom: Flow<Float> = stackAppearanceInteractor.stackBottom.dumpValue("stackBottom")
     /**
      * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
      * further.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 736058a..97b86e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
-import com.android.systemui.common.shared.model.NotificationContainerBounds
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FeatureFlagsClassic
@@ -57,18 +56,6 @@
         interactor.setShadeScrimBounds(bounds)
     }
 
-    /** Notifies that the bounds of the notification placeholder have changed. */
-    fun onStackBoundsChanged(
-        top: Float,
-        bottom: Float,
-    ) {
-        keyguardInteractor.setNotificationContainerBounds(
-            NotificationContainerBounds(top = top, bottom = bottom)
-        )
-        interactor.setStackTop(top)
-        interactor.setStackBottom(bottom)
-    }
-
     /** Sets the available space */
     fun onConstrainedAvailableSpaceChanged(height: Int) {
         interactor.setConstrainedAvailableSpace(height)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 0ba7b3c..3393321 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
@@ -64,6 +65,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
@@ -295,8 +297,7 @@
         return combine(
                 isOnLockscreenWithoutShade,
                 keyguardTransitionInteractor.isInTransition(
-                    from = LOCKSCREEN,
-                    to = AOD,
+                    edge = Edge.create(from = LOCKSCREEN, to = AOD)
                 ),
                 ::Pair
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 7d97428..8fb552f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -283,12 +283,11 @@
     void awakenDreams();
 
     /**
-     * Handle a touch event while dreaming or on the glanceable hub when the touch was initiated
-     * within a prescribed swipeable area. This method is provided for cases where swiping in
-     * certain areas should be handled by CentralSurfaces instead (e.g. swiping hub open, opening
-     * the notification shade over dream or hub).
+     * Handle a touch event while dreaming when the touch was initiated within a prescribed
+     * swipeable area. This method is provided for cases where swiping in certain areas of a dream
+     * should be handled by CentralSurfaces instead (e.g. swiping communal hub open).
      */
-    void handleExternalShadeWindowTouch(MotionEvent event);
+    void handleDreamTouch(MotionEvent event);
 
     boolean isBouncerShowing();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index e93c0f6..7dac77e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -59,6 +59,7 @@
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeHeaderController;
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -82,6 +83,7 @@
     private final com.android.systemui.shade.ShadeController mShadeController;
     private final CommandQueue mCommandQueue;
     private final PanelExpansionInteractor mPanelExpansionInteractor;
+    private final Lazy<ShadeInteractor> mShadeInteractorLazy;
     private final ShadeHeaderController mShadeHeaderController;
     private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
     private final MetricsLogger mMetricsLogger;
@@ -121,6 +123,7 @@
             ShadeController shadeController,
             CommandQueue commandQueue,
             PanelExpansionInteractor panelExpansionInteractor,
+            Lazy<ShadeInteractor> shadeInteractorLazy,
             ShadeHeaderController shadeHeaderController,
             RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
             MetricsLogger metricsLogger,
@@ -148,6 +151,7 @@
         mShadeController = shadeController;
         mCommandQueue = commandQueue;
         mPanelExpansionInteractor = panelExpansionInteractor;
+        mShadeInteractorLazy = shadeInteractorLazy;
         mShadeHeaderController = shadeHeaderController;
         mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
         mMetricsLogger = metricsLogger;
@@ -487,14 +491,23 @@
     }
 
     @Override
-    public void togglePanel() {
-        if (mPanelExpansionInteractor.isPanelExpanded()) {
+    public void toggleNotificationsPanel() {
+        if (mShadeInteractorLazy.get().isAnyExpanded().getValue()) {
             mShadeController.animateCollapseShade();
         } else {
             mShadeController.animateExpandShade();
         }
     }
 
+    @Override
+    public void toggleQuickSettingsPanel() {
+        if (mShadeInteractorLazy.get().isQsExpanded().getValue()) {
+            mShadeController.animateCollapseShade();
+        } else {
+            mShadeController.animateExpandQs();
+        }
+    }
+
     private boolean isGoingToSleep() {
         return mWakefulnessLifecycle.getWakefulness()
                 == WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index d5e66ff..8af7ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -79,7 +79,7 @@
     override fun updateScrimController() {}
     override fun shouldIgnoreTouch() = false
     override fun isDeviceInteractive() = false
-    override fun handleExternalShadeWindowTouch(event: MotionEvent?) {}
+    override fun handleDreamTouch(event: MotionEvent?) {}
     override fun awakenDreams() {}
     override fun isBouncerShowing() = false
     override fun isBouncerShowingScrimmed() = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 23674b2..d3d2b1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.app.StatusBarManager.DISABLE_HOME;
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.WindowVisibleState;
@@ -1006,14 +1007,8 @@
                 // this handling this post-init task. We force an update in this case, and use a new
                 // token to not conflict with any other disabled flags already requested by SysUI
                 Binder token = new Binder();
-                int userId = mContext.getUserId();
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                info.setNavigationHomeDisabled(true);
-                mBarService.disableForUser(info, token, mContext.getPackageName(),
-                        userId, "set the initial view visibility");
-
-                mBarService.disableForUser(new StatusBarManager.DisableInfo(), token,
-                        mContext.getPackageName(), userId, "set the initial view visibility");
+                mBarService.disable(DISABLE_HOME, token, mContext.getPackageName());
+                mBarService.disable(0, token, mContext.getPackageName());
             } catch (RemoteException ex) {
                 ex.rethrowFromSystemServer();
             }
@@ -2810,7 +2805,16 @@
         mScrimController.setExpansionAffectsAlpha(!unlocking);
 
         if (mAlternateBouncerInteractor.isVisibleState()) {
-            if (!DeviceEntryUdfpsRefactor.isEnabled()) {
+            if (DeviceEntryUdfpsRefactor.isEnabled()) {
+                if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
+                        && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+                        || mTransitionToFullShadeProgress > 0f)) {
+                    // Assume scrim state for shade is already correct and do nothing
+                } else {
+                    // Safeguard which prevents the scrim from being stuck in the wrong state
+                    mScrimController.transitionTo(ScrimState.KEYGUARD);
+                }
+            } else {
                 if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
                         && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
                         || mTransitionToFullShadeProgress > 0f)) {
@@ -2819,7 +2823,6 @@
                     mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
                 }
             }
-
             // This will cancel the keyguardFadingAway animation if it is running. We need to do
             // this as otherwise it can remain pending and leave keyguard in a weird state.
             mUnlockScrimCallback.onCancelled();
@@ -2937,8 +2940,8 @@
     };
 
     @Override
-    public void handleExternalShadeWindowTouch(MotionEvent event) {
-        getNotificationShadeWindowViewController().handleExternalTouch(event);
+    public void handleDreamTouch(MotionEvent event) {
+        getNotificationShadeWindowViewController().handleDreamTouch(event);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index f219b9d..2b26e3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -54,7 +54,6 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.ShadeViewStateProvider;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.disableflags.DisableStateTracker;
@@ -133,9 +132,6 @@
     private View mSystemIconsContainer;
     private final StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory;
 
-    // TODO(b/273443374): remove
-    private NotificationMediaManager mNotificationMediaManager;
-
     private final ConfigurationController.ConfigurationListener mConfigurationListener =
             new ConfigurationController.ConfigurationListener() {
                 @Override
@@ -302,7 +298,6 @@
             @Main Executor mainExecutor,
             @Background Executor backgroundExecutor,
             KeyguardLogger logger,
-            NotificationMediaManager notificationMediaManager,
             StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory
     ) {
         super(view);
@@ -357,7 +352,6 @@
                 /* mask2= */ DISABLE2_SYSTEM_ICONS,
                 this::updateViewState
         );
-        mNotificationMediaManager = notificationMediaManager;
         mStatusOverlayHoverListenerFactory = statusOverlayHoverListenerFactory;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 74182fc..fe001b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -64,6 +64,7 @@
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.Edge;
 import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.keyguard.shared.model.ScrimAlpha;
 import com.android.systemui.keyguard.shared.model.TransitionState;
@@ -71,6 +72,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.model.Scenes;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
@@ -454,23 +456,32 @@
                 };
 
         // PRIMARY_BOUNCER->GONE
-        collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(PRIMARY_BOUNCER, GONE),
+        collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(
+                Edge.Companion.create(PRIMARY_BOUNCER, GONE)),
                 mBouncerToGoneTransition, mMainDispatcher);
         collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(),
                 mScrimAlphaConsumer, mMainDispatcher);
 
         // ALTERNATE_BOUNCER->GONE
-        collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(ALTERNATE_BOUNCER, GONE),
+        collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(
+                Edge.Companion.create(ALTERNATE_BOUNCER, Scenes.Gone),
+                Edge.Companion.create(ALTERNATE_BOUNCER, GONE)),
                 mBouncerToGoneTransition, mMainDispatcher);
         collectFlow(behindScrim, mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha(),
                 mScrimAlphaConsumer, mMainDispatcher);
 
         // LOCKSCREEN<->GLANCEABLE_HUB
+        collectFlow(
+                behindScrim,
+                mKeyguardTransitionInteractor.transition(
+                        Edge.Companion.create(LOCKSCREEN, Scenes.Communal),
+                        Edge.Companion.create(LOCKSCREEN, GLANCEABLE_HUB)),
+                mGlanceableHubConsumer,
+                mMainDispatcher);
         collectFlow(behindScrim,
-                mKeyguardTransitionInteractor.transition(LOCKSCREEN, GLANCEABLE_HUB),
-                mGlanceableHubConsumer, mMainDispatcher);
-        collectFlow(behindScrim,
-                mKeyguardTransitionInteractor.transition(GLANCEABLE_HUB, LOCKSCREEN),
+                mKeyguardTransitionInteractor.transition(
+                        Edge.Companion.create(Scenes.Communal, LOCKSCREEN),
+                        Edge.Companion.create(GLANCEABLE_HUB, LOCKSCREEN)),
                 mGlanceableHubConsumer, mMainDispatcher);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f0dab3b..fa88be5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -696,6 +696,7 @@
         Trace.beginSection("StatusBarKeyguardViewManager#show");
         mNotificationShadeWindowController.setKeyguardShowing(true);
         if (SceneContainerFlag.isEnabled()) {
+            // TODO(b/336581871): add sceneState?
             mSceneInteractorLazy.get().changeScene(
                     Scenes.Lockscreen, "StatusBarKeyguardViewManager.show");
         }
@@ -1548,7 +1549,8 @@
         }
 
         if (KeyguardWmStateRefactor.isEnabled()) {
-            mKeyguardTransitionInteractor.startDismissKeyguardTransition();
+            mKeyguardTransitionInteractor.startDismissKeyguardTransition(
+                    "SBKVM#keyguardAuthenticated");
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index a6284e3..4505a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -196,7 +196,22 @@
                 // The group isn't expanded, let's make sure it's visible!
                 mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
             }
-            row.setUserExpanded(true);
+
+            if (android.app.Flags.compactHeadsUpNotificationReply()
+                    && row.isCompactConversationHeadsUpOnScreen()) {
+                // Notification can be system expanded true and it is set user expanded in
+                // activateRemoteInput. notifyHeightChanged also doesn't work as visibleType doesn't
+                // change. To expand huning notification properly, we need set userExpanded false.
+                if (!row.isPinned() && row.isExpanded()) {
+                    row.setUserExpanded(false);
+                }
+                // expand notification emits expanded information to HUN listener.
+                row.expandNotification();
+            } else {
+                // Note: Since Normal HUN has remote input view in it, we don't expect to hit
+                // onMakeExpandedVisibleForRemoteInput from activateRemoteInput for Normal HUN.
+                row.setUserExpanded(true);
+            }
             row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
         }
     }
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 4fc11df..a858fb0 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
@@ -119,7 +119,7 @@
     private MultiSourceMinAlphaController mEndSideAlphaController;
     private LinearLayout mEndSideContent;
     private View mClockView;
-    private View mOngoingCallChip;
+    private View mOngoingActivityChip;
     private View mNotificationIconAreaInner;
     // Visibilities come in from external system callers via disable flags, but we also sometimes
     // modify the visibilities internally. We need to store both so that we don't accidentally
@@ -345,7 +345,7 @@
         mEndSideContent = mStatusBar.findViewById(R.id.status_bar_end_side_content);
         mEndSideAlphaController = new MultiSourceMinAlphaController(mEndSideContent);
         mClockView = mStatusBar.findViewById(R.id.clock);
-        mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
+        mOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip);
         showEndSideContent(false);
         showClock(false);
         initOperatorName();
@@ -594,9 +594,9 @@
         // so if the icons are disabled then the call chip should be, too.)
         boolean showOngoingCallChip = hasOngoingCall && !disableNotifications;
         if (showOngoingCallChip) {
-            showOngoingCallChip(animate);
+            showOngoingActivityChip(animate);
         } else {
-            hideOngoingCallChip(animate);
+            hideOngoingActivityChip(animate);
         }
         mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip);
     }
@@ -688,14 +688,19 @@
         animateShow(mClockView, animate);
     }
 
-    /** Hides the ongoing call chip. */
-    public void hideOngoingCallChip(boolean animate) {
-        animateHiddenState(mOngoingCallChip, View.GONE, animate);
+    /** Hides the ongoing activity chip. */
+    private void hideOngoingActivityChip(boolean animate) {
+        animateHiddenState(mOngoingActivityChip, View.GONE, animate);
     }
 
-    /** Displays the ongoing call chip. */
-    public void showOngoingCallChip(boolean animate) {
-        animateShow(mOngoingCallChip, animate);
+    /**
+     * Displays the ongoing activity chip.
+     *
+     * For now, this chip will only ever contain the ongoing call information, but after b/332662551
+     * feature is implemented, it will support different kinds of ongoing activities.
+     */
+    private void showOngoingActivityChip(boolean animate) {
+        animateShow(mOngoingActivityChip, animate);
     }
 
     /**
@@ -803,7 +808,7 @@
 
     private void initOngoingCallChip() {
         mOngoingCallController.addCallback(mOngoingCallListener);
-        mOngoingCallController.setChipView(mOngoingCallChip);
+        mOngoingCallController.setChipView(mOngoingActivityChip);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index ec88b6c..a7d4ce3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -36,6 +36,8 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
 import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -145,8 +147,8 @@
     fun setChipView(chipView: View) {
         tearDownChipView()
         this.chipView = chipView
-        val backgroundView: OngoingCallBackgroundContainer? =
-            chipView.findViewById(R.id.ongoing_call_chip_background)
+        val backgroundView: ChipBackgroundContainer? =
+            chipView.findViewById(R.id.ongoing_activity_chip_background)
         backgroundView?.maxHeightFetcher = { statusBarWindowController.statusBarHeight }
         if (hasOngoingCall()) {
             updateChip()
@@ -226,7 +228,7 @@
         if (callNotificationInfo == null) { return }
         val currentChipView = chipView
         val backgroundView =
-            currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background)
+            currentChipView?.findViewById<View>(R.id.ongoing_activity_chip_background)
         val intent = callNotificationInfo?.intent
         if (currentChipView != null && backgroundView != null && intent != null) {
             currentChipView.setOnClickListener {
@@ -271,8 +273,8 @@
     @VisibleForTesting
     fun tearDownChipView() = chipView?.getTimeView()?.stop()
 
-    private fun View.getTimeView(): OngoingCallChronometer? {
-        return this.findViewById(R.id.ongoing_call_chip_time)
+    private fun View.getTimeView(): ChipChronometer? {
+        return this.findViewById(R.id.ongoing_activity_chip_time)
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
index 9c78ab4..886481e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index d4b2dbf..2e54972 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -53,6 +53,27 @@
         )
     }
 
+    fun logTopLevelServiceStateBroadcastEmergencyOnly(subId: Int, serviceState: ServiceState) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                bool1 = serviceState.isEmergencyOnly
+            },
+            { "ACTION_SERVICE_STATE for subId=$int1. ServiceState.isEmergencyOnly=$bool1" }
+        )
+    }
+
+    fun logTopLevelServiceStateBroadcastMissingExtras(subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { int1 = subId },
+            { "ACTION_SERVICE_STATE for subId=$int1. Intent is missing extras. Ignoring" }
+        )
+    }
+
     fun logOnSignalStrengthsChanged(signalStrength: SignalStrength, subId: Int) {
         buffer.log(
             TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt
new file mode 100644
index 0000000..cce3eb0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.data.model
+
+import android.telephony.ServiceState
+
+/**
+ * Simplified representation of a [ServiceState] for use in SystemUI. Add any fields that we need to
+ * extract from service state here for consumption downstream
+ */
+data class ServiceStateModel(val isEmergencyOnly: Boolean) {
+    companion object {
+        fun fromServiceState(serviceState: ServiceState): ServiceStateModel {
+            return ServiceStateModel(isEmergencyOnly = serviceState.isEmergencyOnly)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index 9471574..5ad8bf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -21,6 +21,7 @@
 import com.android.settingslib.SignalIcon.MobileIconGroup
 import com.android.settingslib.mobile.MobileMappings
 import com.android.settingslib.mobile.MobileMappings.Config
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
@@ -92,6 +93,19 @@
     val defaultMobileIconGroup: Flow<MobileIconGroup>
 
     /**
+     * [deviceServiceState] is equivalent to the last [Intent.ACTION_SERVICE_STATE] broadcast with a
+     * subscriptionId of -1 (aka [SubscriptionManager.INVALID_SUBSCRIPTION_ID]).
+     *
+     * While each [MobileConnectionsRepository] listens for the service state of each subscription,
+     * there is potentially a service state associated with the device itself. This value can be
+     * used to calculate e.g., the emergency calling capability of the device (as opposed to the
+     * emergency calling capability of an individual mobile connection)
+     *
+     * Note: this is a [StateFlow] using an eager sharing strategy.
+     */
+    val deviceServiceState: StateFlow<ServiceStateModel?>
+
+    /**
      * If any active SIM on the device is in
      * [android.telephony.TelephonyManager.SIM_STATE_PIN_REQUIRED] or
      * [android.telephony.TelephonyManager.SIM_STATE_PUK_REQUIRED] or
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index 8a8e33e..b068152 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
@@ -151,6 +152,15 @@
     override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
         activeRepo.flatMapLatest { it.defaultMobileIconGroup }
 
+    override val deviceServiceState: StateFlow<ServiceStateModel?> =
+        activeRepo
+            .flatMapLatest { it.deviceServiceState }
+            .stateIn(
+                scope,
+                SharingStarted.WhileSubscribed(),
+                realRepository.deviceServiceState.value
+            )
+
     override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure }
     override fun getIsAnySimSecure(): Boolean = activeRepo.value.getIsAnySimSecure()
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index 2b3c632..a944e91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.log.table.TableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
@@ -136,6 +137,9 @@
 
     override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G)
 
+    // TODO(b/339023069): demo command for device-based connectivity state
+    override val deviceServiceState: StateFlow<ServiceStateModel?> = MutableStateFlow(null)
+
     override val isAnySimSecure: Flow<Boolean> = flowOf(getIsAnySimSecure())
     override fun getIsAnySimSecure(): Boolean = false
 
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 0073e9c..c32f0e8 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
@@ -18,8 +18,10 @@
 
 import android.annotation.SuppressLint
 import android.content.Context
+import android.content.Intent
 import android.content.IntentFilter
 import android.telephony.CarrierConfigManager
+import android.telephony.ServiceState
 import android.telephony.SubscriptionInfo
 import android.telephony.SubscriptionManager
 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
@@ -35,7 +37,6 @@
 import com.android.settingslib.mobile.MobileMappings.Config
 import com.android.systemui.Dumpable
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -47,6 +48,7 @@
 import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
 import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
@@ -55,6 +57,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import java.io.PrintWriter
 import java.lang.ref.WeakReference
 import javax.inject.Inject
@@ -68,6 +71,7 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
@@ -169,6 +173,35 @@
             }
             .flowOn(bgDispatcher)
 
+    /** Note that this flow is eager, so we don't miss any state */
+    override val deviceServiceState: StateFlow<ServiceStateModel?> =
+        broadcastDispatcher
+            .broadcastFlow(IntentFilter(Intent.ACTION_SERVICE_STATE)) { intent, _ ->
+                val subId =
+                    intent.getIntExtra(
+                        SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
+                        INVALID_SUBSCRIPTION_ID
+                    )
+
+                val extras = intent.extras
+                if (extras == null) {
+                    logger.logTopLevelServiceStateBroadcastMissingExtras(subId)
+                    return@broadcastFlow null
+                }
+
+                val serviceState = ServiceState.newFromBundle(extras)
+                logger.logTopLevelServiceStateBroadcastEmergencyOnly(subId, serviceState)
+                if (subId == INVALID_SUBSCRIPTION_ID) {
+                    // Assume that -1 here is the device's service state. We don't care about
+                    // other ones.
+                    ServiceStateModel.fromServiceState(serviceState)
+                } else {
+                    null
+                }
+            }
+            .filterNotNull()
+            .stateIn(scope, SharingStarted.Eagerly, null)
+
     /**
      * State flow that emits the set of mobile data subscriptions, each represented by its own
      * [SubscriptionModel].
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 91d7ca6..cc4d568 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -111,6 +111,13 @@
     val isForceHidden: Flow<Boolean>
 
     /**
+     * True if the device-level service state (with -1 subscription id) reports emergency calls
+     * only. This value is only useful when there are no other subscriptions OR all existing
+     * subscriptions report that they are not in service.
+     */
+    val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean>
+
+    /**
      * Vends out a [MobileIconInteractor] tracking the [MobileConnectionRepository] for the given
      * subId.
      */
@@ -377,6 +384,9 @@
             .map { it.contains(ConnectivitySlot.MOBILE) }
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
+    override val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean> =
+        mobileConnectionsRepo.deviceServiceState.map { it?.isEmergencyOnly ?: false }
+
     /** Vends out new [MobileIconInteractor] for a particular subId */
     override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
         reuseCache[subId]?.get() ?: createMobileConnectionInteractorForSubId(subId)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index 51c053e..5b954b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -19,6 +19,9 @@
 import com.android.internal.telephony.flags.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.OemSatelliteInputLog
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
@@ -45,6 +48,7 @@
     deviceProvisioningInteractor: DeviceProvisioningInteractor,
     wifiInteractor: WifiInteractor,
     @Application scope: CoroutineScope,
+    @OemSatelliteInputLog private val logBuffer: LogBuffer,
 ) {
     /** Must be observed by any UI showing Satellite iconography */
     val isSatelliteAllowed =
@@ -79,25 +83,52 @@
     val isWifiActive: Flow<Boolean> =
         wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active }
 
+    private val allConnectionsOos =
+        iconsInteractor.icons.aggregateOver(
+            selector = { intr ->
+                combine(intr.isInService, intr.isEmergencyOnly, intr.isNonTerrestrial) {
+                    isInService,
+                    isEmergencyOnly,
+                    isNtn ->
+                    !isInService && !isEmergencyOnly && !isNtn
+                }
+            },
+            defaultValue = true, // no connections == everything is OOS
+        ) { isOosAndNotEmergencyAndNotSatellite ->
+            isOosAndNotEmergencyAndNotSatellite.all { it }
+        }
+
     /** When all connections are considered OOS, satellite connectivity is potentially valid */
     val areAllConnectionsOutOfService =
         if (Flags.oemEnabledSatelliteFlag()) {
-                iconsInteractor.icons.aggregateOver(
-                    selector = { intr ->
-                        combine(intr.isInService, intr.isEmergencyOnly, intr.isNonTerrestrial) {
-                            isInService,
-                            isEmergencyOnly,
-                            isNtn ->
-                            !isInService && !(isEmergencyOnly || isNtn)
-                        }
-                    }
-                ) { isOosAndNotEmergencyOnlyOrSatellite ->
-                    isOosAndNotEmergencyOnlyOrSatellite.all { it }
+                combine(
+                    allConnectionsOos,
+                    iconsInteractor.isDeviceInEmergencyCallsOnlyMode,
+                ) { connectionsOos, deviceEmergencyOnly ->
+                    logBuffer.log(
+                        TAG,
+                        LogLevel.INFO,
+                        {
+                            bool1 = connectionsOos
+                            bool2 = deviceEmergencyOnly
+                        },
+                        {
+                            "Updating OOS status. allConnectionsOOs=$bool1 " +
+                                "deviceEmergencyOnly=$bool2"
+                        },
+                    )
+                    // If no connections exist, or all are OOS, then we look to the device-based
+                    // service state to detect if any calls are possible
+                    connectionsOos && !deviceEmergencyOnly
                 }
             } else {
                 flowOf(false)
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), true)
+
+    companion object {
+        const val TAG = "DeviceBasedSatelliteInteractor"
+    }
 }
 
 /**
@@ -106,12 +137,22 @@
  *
  * Provides a way to connect the reactivity of the top-level flow with the reactivity of an
  * arbitrarily-defined relationship ([selector]) from R to the flow that R exposes.
+ *
+ * [defaultValue] allows for a default value to be used if there are no leaf nodes after applying
+ * [selector]. E.g., if there are no mobile connections, assume that there is no service.
  */
 @OptIn(ExperimentalCoroutinesApi::class)
 private inline fun <R, reified S, T> Flow<List<R>>.aggregateOver(
     crossinline selector: (R) -> Flow<S>,
-    crossinline transform: (Array<S>) -> T
+    defaultValue: T,
+    crossinline transform: (Array<S>) -> T,
 ): Flow<T> {
     return map { list -> list.map { selector(it) } }
-        .flatMapLatest { newFlows -> combine(newFlows) { newVals -> transform(newVals) } }
+        .flatMapLatest { newFlows ->
+            if (newFlows.isEmpty()) {
+                flowOf(defaultValue)
+            } else {
+                combine(newFlows) { newVals -> transform(newVals) }
+            }
+        }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
index cc87e8a..0a6e95e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
@@ -81,12 +82,12 @@
 ) : CollapsedStatusBarViewModel {
     override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
         keyguardTransitionInteractor
-            .isInTransition(LOCKSCREEN, OCCLUDED)
+            .isInTransition(Edge.create(from = LOCKSCREEN, to = OCCLUDED))
             .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false)
 
     override val transitionFromLockscreenToDreamStartedEvent: Flow<Unit> =
         keyguardTransitionInteractor
-            .transition(LOCKSCREEN, DREAMING)
+            .transition(Edge.create(from = LOCKSCREEN, to = DREAMING))
             .filter { it.transitionState == TransitionState.STARTED }
             .map {}
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index fa8a7d8..8b48bd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
 import com.android.systemui.statusbar.policy.BaseHeadsUpManager.HeadsUpEntry
+import com.android.systemui.util.Compile
 import java.io.PrintWriter
 import javax.inject.Inject
 
@@ -30,12 +31,14 @@
  * succession, by delaying visual listener side effects and removal handling from BaseHeadsUpManager
  */
 @SysUISingleton
-class AvalancheController @Inject constructor(
+class AvalancheController
+@Inject
+constructor(
     dumpManager: DumpManager,
 ) : Dumpable {
 
     private val tag = "AvalancheController"
-    private val debug = false
+    private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG)
 
     // HUN showing right now, in the floating state where full shade is hidden, on launcher or AOD
     @VisibleForTesting var headsUpEntryShowing: HeadsUpEntry? = null
@@ -79,7 +82,7 @@
         val fn = "[$label] => AvalancheController.update [${getKey(entry)}]"
         if (entry == null) {
             log { "Entry is NULL, stop update." }
-            return;
+            return
         }
         if (debug) {
             debugRunnableLabelMap[runnable] = label
@@ -106,7 +109,10 @@
             if (isOnlyNextEntry) {
                 // HeadsUpEntry.updateEntry recursively calls AvalancheController#update
                 // and goes to the isShowing case above
-                headsUpEntryShowing!!.updateEntry(false, "avalanche duration update")
+                headsUpEntryShowing!!.updateEntry(
+                        /* updatePostTime= */ false,
+                        /* updateEarliestRemovalTime= */ false,
+                        /* reason= */ "avalanche duration update")
             }
         }
         logState("after $fn")
@@ -142,9 +148,12 @@
         } else if (isShowing(entry)) {
             log { "$fn => [remove showing ${getKey(entry)}]" }
             previousHunKey = getKey(headsUpEntryShowing)
-
+            // Show the next HUN before removing this one, so that we don't tell listeners
+            // onHeadsUpPinnedModeChanged, which causes
+            // NotificationPanelViewController.updateTouchableRegion to hide the window while the
+            // HUN is animating out, resulting in a flicker.
+            showNext()
             runnable.run()
-            showNextAfterRemove()
         } else {
             log { "$fn => [removing untracked ${getKey(entry)}]" }
         }
@@ -247,7 +256,7 @@
         }
     }
 
-    private fun showNextAfterRemove() {
+    private fun showNext() {
         log { "SHOW NEXT" }
         headsUpEntryShowing = null
 
@@ -294,17 +303,21 @@
 
     private fun getStateStr(): String {
         return "SHOWING: [${getKey(headsUpEntryShowing)}]" +
-                "\nPREVIOUS: [$previousHunKey]" +
-                "\nNEXT LIST: $nextListStr" +
-                "\nNEXT MAP: $nextMapStr" +
-                "\nDROPPED: $dropSetStr"
+            "\nPREVIOUS: [$previousHunKey]" +
+            "\nNEXT LIST: $nextListStr" +
+            "\nNEXT MAP: $nextMapStr" +
+            "\nDROPPED: $dropSetStr"
     }
 
     private fun logState(reason: String) {
-        log { "\n================================================================================="}
+        log {
+            "\n================================================================================="
+        }
         log { "STATE $reason" }
         log { getStateStr() }
-        log { "=================================================================================\n"}
+        log {
+            "=================================================================================\n"
+        }
     }
 
     private val dropSetStr: String
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index b8318a7..a7fe49b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -39,6 +39,7 @@
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
 import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
 import com.android.systemui.util.ListenerSet;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -114,7 +115,8 @@
         mUiEventLogger = uiEventLogger;
         mAvalancheController = avalancheController;
         Resources resources = context.getResources();
-        mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
+        mMinimumDisplayTime = NotificationThrottleHun.isEnabled()
+                ? 500 : resources.getInteger(R.integer.heads_up_notification_minimum_time);
         mStickyForSomeTimeAutoDismissTime = resources.getInteger(
                 R.integer.sticky_heads_up_notification_time);
         mAutoDismissTime = resources.getInteger(R.integer.heads_up_notification_decay);
@@ -765,11 +767,23 @@
          * @param updatePostTime whether or not to refresh the post time
          */
         public void updateEntry(boolean updatePostTime, @Nullable String reason) {
+            updateEntry(updatePostTime, /* updateEarliestRemovalTime= */ true, reason);
+        }
+
+        /**
+         * Updates an entry's removal time.
+         * @param updatePostTime whether or not to refresh the post time
+         * @param updateEarliestRemovalTime whether this update should further delay removal
+         */
+        public void updateEntry(boolean updatePostTime, boolean updateEarliestRemovalTime,
+                @Nullable String reason) {
             Runnable runnable = () -> {
                 mLogger.logUpdateEntry(mEntry, updatePostTime, reason);
 
                 final long now = mSystemClock.elapsedRealtime();
-                mEarliestRemovalTime = now + mMinimumDisplayTime;
+                if (updateEarliestRemovalTime) {
+                    mEarliestRemovalTime = now + mMinimumDisplayTime;
+                }
 
                 if (updatePostTime) {
                     mPostTime = Math.max(mPostTime, now);
@@ -785,7 +799,9 @@
             FinishTimeUpdater finishTimeCalculator = () -> {
                 final long finishTime = calculateFinishTime();
                 final long now = mSystemClock.elapsedRealtime();
-                final long timeLeft = Math.max(finishTime - now, mMinimumDisplayTime);
+                final long timeLeft = NotificationThrottleHun.isEnabled()
+                        ? Math.max(finishTime, mEarliestRemovalTime) - now
+                        : Math.max(finishTime - now, mMinimumDisplayTime);
                 return timeLeft;
             };
             scheduleAutoRemovalCallback(finishTimeCalculator, "updateEntry (not sticky)");
@@ -818,13 +834,6 @@
         }
 
         public int compareNonTimeFields(HeadsUpEntry headsUpEntry) {
-            boolean isPinned = mEntry.isRowPinned();
-            boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
-            if (isPinned && !otherPinned) {
-                return -1;
-            } else if (!isPinned && otherPinned) {
-                return 1;
-            }
             boolean selfFullscreen = hasFullScreenIntent(mEntry);
             boolean otherFullscreen = hasFullScreenIntent(headsUpEntry.mEntry);
             if (selfFullscreen && !otherFullscreen) {
@@ -851,6 +860,13 @@
         }
 
         public int compareTo(@NonNull HeadsUpEntry headsUpEntry) {
+            boolean isPinned = mEntry.isRowPinned();
+            boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
+            if (isPinned && !otherPinned) {
+                return -1;
+            } else if (!isPinned && otherPinned) {
+                return 1;
+            }
             int nonTimeCompareResult = compareNonTimeFields(headsUpEntry);
             if (nonTimeCompareResult != 0) {
                 return nonTimeCompareResult;
diff --git a/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java b/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java
new file mode 100644
index 0000000..efeb2f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.theme;
+
+import com.google.ux.material.libmonet.dynamiccolor.ContrastCurve;
+import com.google.ux.material.libmonet.dynamiccolor.DynamicColor;
+import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
+import com.google.ux.material.libmonet.dynamiccolor.ToneDeltaPair;
+import com.google.ux.material.libmonet.dynamiccolor.TonePolarity;
+
+class CustomDynamicColors {
+    private final MaterialDynamicColors mMdc;
+
+    CustomDynamicColors(boolean isExtendedFidelity) {
+        this.mMdc = new MaterialDynamicColors(isExtendedFidelity);
+    }
+
+    // CLOCK COLORS
+
+    public DynamicColor widgetBackground() {
+        return new DynamicColor(
+                /* name= */ "widget_background",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 20.0 : 95.0,
+                /* isBackground= */ true,
+                /* background= */ null,
+                /* secondBackground= */ null,
+                /* contrastCurve= */ null,
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor clockHour() {
+        return new DynamicColor(
+                /* name= */ "clock_hour",
+                /* palette= */ (s) -> s.secondaryPalette,
+                /* tone= */ (s) -> s.isDark ? 30.0 : 60.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> widgetBackground(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 4.0, 5.0, 15.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(clockHour(), clockMinute(), 10.0, TonePolarity.DARKER,
+                        false));
+    }
+
+    public DynamicColor clockMinute() {
+        return new DynamicColor(
+                /* name= */ "clock_minute",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 40.0 : 90.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> widgetBackground(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 6.5, 10.0, 15.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor clockSecond() {
+        return new DynamicColor(
+                /* name= */ "clock_second",
+                /* palette= */ (s) -> s.tertiaryPalette,
+                /* tone= */ (s) -> s.isDark ? 40.0 : 90.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> widgetBackground(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 5.0, 70.0, 11.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor weatherTemp() {
+        return new DynamicColor(
+                /* name= */ "clock_second",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 55.0 : 80.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> widgetBackground(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 5.0, 70.0, 11.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    // THEME APP ICONS
+
+    public DynamicColor themeApp() {
+        return new DynamicColor(
+                /* name= */ "theme_app",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 90.0 : 30.0, // Adjusted values
+                /* isBackground= */ true,
+                /* background= */ null,
+                /* secondBackground= */ null,
+                /* contrastCurve= */ null,
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor onThemeApp() {
+        return new DynamicColor(
+                /* name= */ "on_theme_app",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 40.0 : 80.0, // Adjusted values
+                /* isBackground= */ false,
+                /* background= */ (s) -> themeApp(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 7.0, 10.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor themeAppRing() {
+        return new DynamicColor(
+                /* name= */ "theme_app_ring",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> 70.0,
+                /* isBackground= */ true,
+                /* background= */ null,
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor themeNotif() {
+        return new DynamicColor(
+                /* name= */ "theme_notif",
+                /* palette= */ (s) -> s.tertiaryPalette,
+                /* tone= */ (s) -> s.isDark ? 80.0 : 90.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> themeAppRing(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(themeNotif(), themeAppRing(), 10.0, TonePolarity.NEARER,
+                        false));
+    }
+
+    // SUPER G COLORS
+
+    public DynamicColor brandA() {
+        return new DynamicColor(
+                /* name= */ "brand_a",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 40.0 : 80.0,
+                /* isBackground= */ true,
+                /* background= */ (s) -> mMdc.surfaceContainerLow(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 7.0, 17.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(brandA(), brandB(), 10.0, TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor brandB() {
+        return new DynamicColor(
+                /* name= */ "brand_b",
+                /* palette= */ (s) -> s.secondaryPalette,
+                /* tone= */ (s) -> s.isDark ? 70.0 : 98.0,
+                /* isBackground= */ true,
+                /* background= */ (s) -> mMdc.surfaceContainerLow(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 3.0, 6.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(brandB(), brandC(), 10.0, TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor brandC() {
+        return new DynamicColor(
+                /* name= */ "brand_c",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 50.0 : 60.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> mMdc.surfaceContainerLow(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.0, 9.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(brandC(), brandD(), 10.0, TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor brandD() {
+        return new DynamicColor(
+                /* name= */ "brand_d",
+                /* palette= */ (s) -> s.tertiaryPalette,
+                /* tone= */ (s) -> s.isDark ? 59.0 : 90.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> mMdc.surfaceContainerLow(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.0, 13.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(brandD(), brandA(), 10.0, TonePolarity.NEARER, false));
+    }
+
+    // QUICK SETTING TIILES
+
+    public DynamicColor underSurface() {
+        return new DynamicColor(
+                /* name= */ "under_surface",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> 0.0,
+                /* isBackground= */ true,
+                /* background= */ null,
+                /* secondBackground= */ null,
+                /* contrastCurve= */ null,
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor shadeActive() {
+        return new DynamicColor(
+                /* name= */ "shade_active",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> 90.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> underSurface(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.5, 7.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(shadeActive(), shadeInactive(), 30.0, TonePolarity.LIGHTER,
+                        false));
+    }
+
+    public DynamicColor onShadeActive() {
+        return new DynamicColor(
+                /* name= */ "on_shade_active",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> 10.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> shadeActive(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(onShadeActive(), onShadeActiveVariant(), 20.0,
+                        TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor onShadeActiveVariant() {
+        return new DynamicColor(
+                /* name= */ "on_shade_active_variant",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> 30.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> shadeActive(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(onShadeActiveVariant(), onShadeActive(), 20.0,
+                        TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor shadeInactive() {
+        return new DynamicColor(
+                /* name= */ "shade_inactive",
+                /* palette= */ (s) -> s.neutralPalette,
+                /* tone= */ (s) -> 20.0,
+                /* isBackground= */ true,
+                /* background= */ (s) -> underSurface(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+                /* toneDeltaPair= */(s) -> new ToneDeltaPair(shadeInactive(), shadeDisabled(), 15.0,
+                TonePolarity.LIGHTER, false));
+    }
+
+    public DynamicColor onShadeInactive() {
+        return new DynamicColor(
+                /* name= */ "on_shade_inactive",
+                /* palette= */ (s) -> s.neutralVariantPalette,
+                /* tone= */ (s) -> 90.0,
+                /* isBackground= */ true,
+                /* background= */ (s) -> shadeInactive(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(onShadeInactive(), onShadeInactiveVariant(), 10.0,
+                        TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor onShadeInactiveVariant() {
+        return new DynamicColor(
+                /* name= */ "on_shade_inactive_variant",
+                /* palette= */ (s) -> s.neutralVariantPalette,
+                /* tone= */ (s) -> 80.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> shadeInactive(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(onShadeInactiveVariant(), onShadeInactive(), 10.0,
+                        TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor shadeDisabled() {
+        return new DynamicColor(
+                /* name= */ "shade_disabled",
+                /* palette= */ (s) -> s.neutralPalette,
+                /* tone= */ (s) -> 4.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> underSurface(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor overviewBackground() {
+        return new DynamicColor(
+                /* name= */ "overview_background",
+                /* palette= */ (s) -> s.neutralVariantPalette,
+                /* tone= */ (s) -> s.isDark ? 80.0 : 35.0,
+                /* isBackground= */ true,
+                /* background= */ null,
+                /* secondBackground= */ null,
+                /* contrastCurve= */null,
+                /* toneDeltaPair= */ null);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
index a983d2f..3518759 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
+++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
@@ -103,5 +103,33 @@
                 Pair.create("on_tertiary_fixed_variant", mdc.onTertiaryFixedVariant()),
             )
         }
+
+        @JvmStatic
+        fun getCustomColorsMapped(isExtendedFidelity: Boolean): List<Pair<String, DynamicColor>> {
+            val customMdc = CustomDynamicColors(isExtendedFidelity)
+            return arrayListOf(
+                Pair.create("widget_background", customMdc.widgetBackground()),
+                Pair.create("clock_hour", customMdc.clockHour()),
+                Pair.create("clock_minute", customMdc.clockMinute()),
+                Pair.create("clock_second", customMdc.weatherTemp()),
+                Pair.create("theme_app", customMdc.themeApp()),
+                Pair.create("on_theme_app", customMdc.onThemeApp()),
+                Pair.create("theme_app_ring", customMdc.themeAppRing()),
+                Pair.create("on_theme_app_ring", customMdc.themeNotif()),
+                Pair.create("brand_a", customMdc.brandA()),
+                Pair.create("brand_b", customMdc.brandB()),
+                Pair.create("brand_c", customMdc.brandC()),
+                Pair.create("brand_d", customMdc.brandD()),
+                Pair.create("under_surface", customMdc.underSurface()),
+                Pair.create("shade_active", customMdc.shadeActive()),
+                Pair.create("on_shade_active", customMdc.onShadeActive()),
+                Pair.create("on_shade_active_variant", customMdc.onShadeActiveVariant()),
+                Pair.create("shade_inactive", customMdc.shadeInactive()),
+                Pair.create("on_shade_inactive", customMdc.onShadeInactive()),
+                Pair.create("on_shade_inactive_variant", customMdc.onShadeInactiveVariant()),
+                Pair.create("shade_disabled", customMdc.shadeDisabled()),
+                Pair.create("overview_background", customMdc.overviewBackground())
+            )
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 5c3bbb7..d256c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -56,6 +56,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
@@ -84,6 +85,7 @@
 import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.SecureSettings;
 
+import com.google.ux.material.libmonet.dynamiccolor.DynamicColor;
 import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
 
 import org.json.JSONException;
@@ -631,29 +633,33 @@
 
     protected FabricatedOverlay createDynamicOverlay() {
         FabricatedOverlay overlay = newFabricatedOverlay("dynamic");
-        assignDynamicPaletteToOverlay(overlay, true /* isDark */);
-        assignDynamicPaletteToOverlay(overlay, false /* isDark */);
-        assignFixedColorsToOverlay(overlay);
+        //Themed Colors
+        assignColorsToOverlay(overlay, DynamicColors.allDynamicColorsMapped(mIsFidelityEnabled),
+                false);
+        // Fixed Colors
+        assignColorsToOverlay(overlay, DynamicColors.getFixedColorsMapped(mIsFidelityEnabled),
+                true);
+        //Custom Colors
+        assignColorsToOverlay(overlay, DynamicColors.getCustomColorsMapped(mIsFidelityEnabled),
+                false);
         return overlay;
     }
 
-    private void assignDynamicPaletteToOverlay(FabricatedOverlay overlay, boolean isDark) {
-        String suffix = isDark ? "dark" : "light";
-        ColorScheme scheme = isDark ? mDarkColorScheme : mLightColorScheme;
-        DynamicColors.allDynamicColorsMapped(mIsFidelityEnabled).forEach(p -> {
-            String resourceName = "android:color/system_" + p.first + "_" + suffix;
-            int colorValue = p.second.getArgb(scheme.getMaterialScheme());
-            overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
-                    null /* configuration */);
-        });
-    }
+    private void assignColorsToOverlay(FabricatedOverlay overlay,
+            List<Pair<String, DynamicColor>> colors, Boolean isFixed) {
+        colors.forEach(p -> {
+            String prefix = "android:color/system_" + p.first;
 
-    private void assignFixedColorsToOverlay(FabricatedOverlay overlay) {
-        DynamicColors.getFixedColorsMapped(mIsFidelityEnabled).forEach(p -> {
-            String resourceName = "android:color/system_" + p.first;
-            int colorValue = p.second.getArgb(mLightColorScheme.getMaterialScheme());
-            overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
-                    null /* configuration */);
+            if (isFixed) {
+                overlay.setResourceValue(prefix, TYPE_INT_COLOR_ARGB8,
+                        p.second.getArgb(mLightColorScheme.getMaterialScheme()), null);
+                return;
+            }
+
+            overlay.setResourceValue(prefix + "_light", TYPE_INT_COLOR_ARGB8,
+                    p.second.getArgb(mLightColorScheme.getMaterialScheme()), null);
+            overlay.setResourceValue(prefix + "_dark", TYPE_INT_COLOR_ARGB8,
+                    p.second.getArgb(mDarkColorScheme.getMaterialScheme()), null);
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
index bfed0c4..0a1724c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
@@ -70,8 +70,10 @@
     val isGuestUserResetting: Boolean = repository.isGuestUserResetting
 
     init {
-        resumeSessionReceiver.register()
-        resetOrExitSessionReceiver.register()
+        if (applicationContext.userId == UserHandle.USER_SYSTEM) {
+            resumeSessionReceiver.register()
+            resetOrExitSessionReceiver.register()
+        }
     }
 
     /** Notifies that the device has finished booting. */
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
index 9339651..516cb46 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
@@ -533,7 +533,7 @@
                 targetUserId = targetUserId,
                 ::showDialog,
                 ::dismissDialog,
-                ::selectUser,
+                ::switchUser
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
index 46ce5f2..1ec86a4 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
@@ -97,3 +97,12 @@
 fun <A, B, R> combineFlows(flow1: Flow<A>, flow2: Flow<B>, bifunction: (A, B) -> R): Flow<R> {
     return combine(flow1, flow2, bifunction)
 }
+
+fun <A, B, C, R> combineFlows(
+    flow1: Flow<A>,
+    flow2: Flow<B>,
+    flow3: Flow<C>,
+    trifunction: (A, B, C) -> R
+): Flow<R> {
+    return combine(flow1, flow2, flow3, trifunction)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index c69fb66..e56893a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -174,9 +174,6 @@
     private static final String TYPE_DISMISS = "dismiss";
     /** Volume dialog slider animation. */
     private static final String TYPE_UPDATE = "update";
-    static final int PROGRESS_HAPTICS_DISABLED = 0;
-    static final int PROGRESS_HAPTICS_EAGER = 1;
-    static final int PROGRESS_HAPTICS_ANIMATED = 2;
 
     /**
      *  TODO(b/290612381): remove lingering animations or tolerate them
@@ -285,7 +282,7 @@
     @GuardedBy("mSafetyWarningLock")
     private CsdWarningDialog mCsdDialog;
     private boolean mHovering = false;
-    private final boolean mShowActiveStreamOnly;
+    private final boolean mIsTv;
     private boolean mConfigChanged = false;
     private boolean mIsAnimatingDismiss = false;
     private boolean mHasSeenODICaptionsTooltip;
@@ -346,7 +343,7 @@
         mConfigurationController = configurationController;
         mMediaOutputDialogManager = mediaOutputDialogManager;
         mCsdWarningDialogFactory = csdWarningDialogFactory;
-        mShowActiveStreamOnly = showActiveStreamOnly();
+        mIsTv = isTv();
         mHasSeenODICaptionsTooltip =
                 Prefs.getBoolean(context, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false);
         mShowLowMediaVolumeIcon =
@@ -1635,7 +1632,7 @@
         Trace.endSection();
     }
 
-    private boolean showActiveStreamOnly() {
+    private boolean isTv() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
                 || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION);
     }
@@ -1647,7 +1644,7 @@
             return true;
         }
 
-        if (!mShowActiveStreamOnly) {
+        if (!mIsTv) {
             if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
                 return mShowA11yStream;
             }
@@ -2092,6 +2089,11 @@
         }
         final int newProgress = getProgressFromVolume(row.ss, row.slider, vlevel);
         if (progress != newProgress) {
+            if (mIsTv) {
+                // don't animate slider on TVs
+                row.slider.setProgress(newProgress, false);
+                return;
+            }
             if (mShowing && rowVisible) {
                 // animate!
                 if (row.anim != null && row.anim.isRunning()
@@ -2112,7 +2114,7 @@
                     row.anim.setIntValues(progress, newProgress);
                     // The animator can't keep up with the volume changes so haptics need to be
                     // triggered here. This happens when the volume keys are continuously pressed.
-                    row.deliverOnProgressChangedHaptics(false, newProgress, PROGRESS_HAPTICS_EAGER);
+                    row.deliverOnProgressChangedHaptics(false, newProgress);
                 }
                 row.animTargetProgress = newProgress;
                 row.anim.setDuration(UPDATE_ANIMATION_DURATION);
@@ -2127,13 +2129,14 @@
         }
     }
 
-    @VisibleForTesting int progressHapticsForStream(int stream) {
+    @VisibleForTesting
+    boolean canDeliverProgressHapticsToStream(int stream, boolean fromUser, int progress) {
         for (VolumeRow row: mRows) {
             if (row.stream == stream) {
-                return row.mProgressHapticsType;
+                return row.deliverOnProgressChangedHaptics(fromUser, progress);
             }
         }
-        return PROGRESS_HAPTICS_DISABLED;
+        return false;
     }
 
     private void recheckH(VolumeRow row) {
@@ -2527,8 +2530,7 @@
                 if (fromUser || mRow.animTargetProgress == progress) {
                     // Deliver user-generated slider haptics immediately, or when the animation
                     // completes
-                    mRow.deliverOnProgressChangedHaptics(
-                            fromUser, progress, PROGRESS_HAPTICS_ANIMATED);
+                    mRow.deliverOnProgressChangedHaptics(fromUser, progress);
                 }
             }
             if (D.BUG) Log.d(TAG, AudioSystem.streamToString(mRow.stream)
@@ -2641,7 +2643,6 @@
         private int animTargetProgress;
         private int lastAudibleLevel = 1;
         private SeekbarHapticPlugin mHapticPlugin;
-        private int mProgressHapticsType = PROGRESS_HAPTICS_DISABLED;
 
         void setIcon(int iconRes, Resources.Theme theme) {
             if (icon != null) {
@@ -2683,15 +2684,23 @@
             slider.setOnTouchListener(null);
         }
 
-        void deliverOnProgressChangedHaptics(boolean fromUser, int progress, int hapticsType) {
-            if (mHapticPlugin == null) return;
+        /**
+         * Deliver haptics when the progress of the slider has changed.
+         *
+         * @param fromUser True if the progress changed was caused by the user.
+         * @param progress The progress value of the slider.
+         * @return True if haptics were successfully delivered. False otherwise. This will happen
+         *   if mHapticPlugin is null
+         */
+        boolean deliverOnProgressChangedHaptics(boolean fromUser, int progress) {
+            if (mHapticPlugin == null) return false;
 
             mHapticPlugin.onProgressChanged(slider, progress, fromUser);
             if (!fromUser) {
                 // Consider a change from program as the volume key being continuously pressed
                 mHapticPlugin.onKeyDown();
             }
-            mProgressHapticsType = hapticsType;
+            return true;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
index 155102c9..3696108 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
 import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactoryImpl
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractorImpl
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -41,6 +43,11 @@
         impl: LocalMediaRepositoryFactoryImpl
     ): LocalMediaRepositoryFactory
 
+    @Binds
+    fun bindMediaControllerInteractor(
+        impl: MediaControllerInteractorImpl
+    ): MediaControllerInteractor
+
     companion object {
 
         @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt
new file mode 100644
index 0000000..4812765
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import android.os.Bundle
+import android.os.Handler
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
+import javax.inject.Inject
+import kotlinx.coroutines.channels.ProducerScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+interface MediaControllerInteractor {
+
+    /** [MediaController.Callback] flow representation. */
+    fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel>
+}
+
+@SysUISingleton
+class MediaControllerInteractorImpl
+@Inject
+constructor(
+    @Background private val backgroundHandler: Handler,
+) : MediaControllerInteractor {
+
+    override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> {
+        return conflatedCallbackFlow {
+            val callback = MediaControllerCallbackProducer(this)
+            mediaController.registerCallback(callback, backgroundHandler)
+            awaitClose { mediaController.unregisterCallback(callback) }
+        }
+    }
+}
+
+private class MediaControllerCallbackProducer(
+    private val producingScope: ProducerScope<MediaControllerChangeModel>
+) : MediaController.Callback() {
+
+    override fun onSessionDestroyed() {
+        send(MediaControllerChangeModel.SessionDestroyed)
+    }
+
+    override fun onSessionEvent(event: String, extras: Bundle?) {
+        send(MediaControllerChangeModel.SessionEvent(event, extras))
+    }
+
+    override fun onPlaybackStateChanged(state: PlaybackState?) {
+        send(MediaControllerChangeModel.PlaybackStateChanged(state))
+    }
+
+    override fun onMetadataChanged(metadata: MediaMetadata?) {
+        send(MediaControllerChangeModel.MetadataChanged(metadata))
+    }
+
+    override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) {
+        send(MediaControllerChangeModel.QueueChanged(queue))
+    }
+
+    override fun onQueueTitleChanged(title: CharSequence?) {
+        send(MediaControllerChangeModel.QueueTitleChanged(title))
+    }
+
+    override fun onExtrasChanged(extras: Bundle?) {
+        send(MediaControllerChangeModel.ExtrasChanged(extras))
+    }
+
+    override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
+        send(MediaControllerChangeModel.AudioInfoChanged(info))
+    }
+
+    private fun send(change: MediaControllerChangeModel) {
+        producingScope.launch { producingScope.send(change) }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
index dc73344..599bd73 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
@@ -18,11 +18,9 @@
 
 import android.media.session.MediaController
 import android.media.session.PlaybackState
-import android.os.Handler
-import com.android.settingslib.volume.data.repository.MediaControllerChange
 import com.android.settingslib.volume.data.repository.MediaControllerRepository
-import com.android.settingslib.volume.data.repository.stateChanges
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
 import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import javax.inject.Inject
@@ -45,38 +43,39 @@
 @Inject
 constructor(
     @Background private val backgroundCoroutineContext: CoroutineContext,
-    @Background private val backgroundHandler: Handler,
+    private val mediaControllerInteractor: MediaControllerInteractor,
     private val mediaControllerRepository: MediaControllerRepository,
 ) {
 
     /** [PlaybackState] changes for the [MediaDeviceSession]. */
     fun playbackState(session: MediaDeviceSession): Flow<PlaybackState?> {
         return stateChanges(session) {
-                emit(MediaControllerChange.PlaybackStateChanged(it.playbackState))
+                emit(MediaControllerChangeModel.PlaybackStateChanged(it.playbackState))
             }
-            .filterIsInstance(MediaControllerChange.PlaybackStateChanged::class)
+            .filterIsInstance(MediaControllerChangeModel.PlaybackStateChanged::class)
             .map { it.state }
     }
 
     /** [MediaController.PlaybackInfo] changes for the [MediaDeviceSession]. */
     fun playbackInfo(session: MediaDeviceSession): Flow<MediaController.PlaybackInfo?> {
         return stateChanges(session) {
-                emit(MediaControllerChange.AudioInfoChanged(it.playbackInfo))
+                emit(MediaControllerChangeModel.AudioInfoChanged(it.playbackInfo))
             }
-            .filterIsInstance(MediaControllerChange.AudioInfoChanged::class)
+            .filterIsInstance(MediaControllerChangeModel.AudioInfoChanged::class)
             .map { it.info }
     }
 
     private fun stateChanges(
         session: MediaDeviceSession,
-        onStart: suspend FlowCollector<MediaControllerChange>.(controller: MediaController) -> Unit,
-    ): Flow<MediaControllerChange?> =
+        onStart:
+            suspend FlowCollector<MediaControllerChangeModel>.(controller: MediaController) -> Unit,
+    ): Flow<MediaControllerChangeModel?> =
         mediaControllerRepository.activeSessions
             .flatMapLatest { controllers ->
                 val controller: MediaController =
                     findControllerForSession(controllers, session)
                         ?: return@flatMapLatest flowOf(null)
-                controller.stateChanges(backgroundHandler).onStart { onStart(controller) }
+                mediaControllerInteractor.stateChanges(controller).onStart { onStart(controller) }
             }
             .flowOn(backgroundCoroutineContext)
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index b00829e..9fbd79a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -19,12 +19,10 @@
 import android.content.pm.PackageManager
 import android.media.VolumeProvider
 import android.media.session.MediaController
-import android.os.Handler
 import android.util.Log
 import com.android.settingslib.media.MediaDevice
 import com.android.settingslib.volume.data.repository.LocalMediaRepository
 import com.android.settingslib.volume.data.repository.MediaControllerRepository
-import com.android.settingslib.volume.data.repository.stateChanges
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
 import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions
@@ -61,7 +59,7 @@
     @VolumePanelScope private val coroutineScope: CoroutineScope,
     @Background private val backgroundCoroutineContext: CoroutineContext,
     mediaControllerRepository: MediaControllerRepository,
-    @Background private val backgroundHandler: Handler,
+    private val mediaControllerInteractor: MediaControllerInteractor,
 ) {
 
     private val activeMediaControllers: Flow<MediaControllers> =
@@ -194,7 +192,10 @@
             return flowOf(null)
         }
 
-        return stateChanges(backgroundHandler).map { this }.onStart { emit(this@stateChanges) }
+        return mediaControllerInteractor
+            .stateChanges(this)
+            .map { this }
+            .onStart { emit(this@stateChanges) }
     }
 
     private data class MediaControllers(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt
new file mode 100644
index 0000000..ef5a44a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.model
+
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import android.os.Bundle
+
+/** Models particular change event received by [MediaController.Callback]. */
+sealed interface MediaControllerChangeModel {
+
+    data object SessionDestroyed : MediaControllerChangeModel
+
+    data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChangeModel
+
+    data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChangeModel
+
+    data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChangeModel
+
+    data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) :
+        MediaControllerChangeModel
+
+    data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChangeModel
+
+    data class ExtrasChanged(val extras: Bundle?) : MediaControllerChangeModel
+
+    data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) :
+        MediaControllerChangeModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
index 26d6a9a..4b4d69a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
@@ -31,13 +31,15 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.flow.transformLatest
 import kotlinx.coroutines.launch
@@ -53,12 +55,39 @@
 constructor(
     @VolumePanelScope private val scope: CoroutineScope,
     mediaOutputInteractor: MediaOutputInteractor,
-    private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
+    mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
     private val streamSliderViewModelFactory: AudioStreamSliderViewModel.Factory,
     private val castVolumeSliderViewModelFactory: CastVolumeSliderViewModel.Factory,
     streamsInteractor: AudioSlidersInteractor,
 ) {
 
+    private val mutableIsExpanded = MutableStateFlow<Boolean?>(null)
+    private val isPlaybackActive: Flow<Boolean?> =
+        mediaOutputInteractor.defaultActiveMediaSession
+            .filterData()
+            .flatMapLatest { session ->
+                if (session == null) {
+                    flowOf(false)
+                } else {
+                    mediaDeviceSessionInteractor.playbackState(session).map { it?.isActive == true }
+                }
+            }
+            .onEach { isPlaybackActive -> mutableIsExpanded.value = !isPlaybackActive }
+            .stateIn(scope, SharingStarted.Eagerly, null)
+    private val portraitExpandable: Flow<SlidersExpandableViewModel> =
+        isPlaybackActive
+            .filterNotNull()
+            .flatMapLatest { isActive ->
+                if (isActive) {
+                    mutableIsExpanded.filterNotNull().map { isExpanded ->
+                        SlidersExpandableViewModel.Expandable(isExpanded)
+                    }
+                } else {
+                    flowOf(SlidersExpandableViewModel.Fixed)
+                }
+            }
+            .stateIn(scope, SharingStarted.Eagerly, SlidersExpandableViewModel.Unavailable)
+
     val sliderViewModels: StateFlow<List<SliderViewModel>> =
         streamsInteractor.volumePanelSliders
             .transformLatest { sliderTypes ->
@@ -76,24 +105,16 @@
             }
             .stateIn(scope, SharingStarted.Eagerly, emptyList())
 
-    private val mutableIsExpanded = MutableSharedFlow<Boolean>()
-
-    val isExpanded: StateFlow<Boolean> =
-        merge(
-                mutableIsExpanded,
-                mediaOutputInteractor.defaultActiveMediaSession.filterData().flatMapLatest { session
-                    ->
-                    if (session == null) flowOf(true)
-                    else
-                        mediaDeviceSessionInteractor.playbackState(session).map {
-                            it?.isActive != true
-                        }
-                },
-            )
-            .stateIn(scope, SharingStarted.Eagerly, false)
+    fun isExpandable(isPortrait: Boolean): Flow<SlidersExpandableViewModel> {
+        return if (isPortrait) {
+            portraitExpandable
+        } else {
+            flowOf(SlidersExpandableViewModel.Fixed)
+        }
+    }
 
     fun onExpandedChanged(isExpanded: Boolean) {
-        scope.launch { mutableIsExpanded.emit(isExpanded) }
+        scope.launch { mutableIsExpanded.value = isExpanded }
     }
 
     private fun CoroutineScope.createSessionViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/SlidersExpandableViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/SlidersExpandableViewModel.kt
new file mode 100644
index 0000000..19b9ead
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/SlidersExpandableViewModel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.volume.ui.viewmodel
+
+/**
+ * Models expandability state of the
+ * [com.android.systemui.volume.panel.component.volume.ui.composable.VolumeSlidersComponent].
+ */
+sealed interface SlidersExpandableViewModel {
+
+    /** [SlidersExpandableViewModel] is not loaded. */
+    data object Unavailable : SlidersExpandableViewModel
+
+    data class Expandable(val isExpanded: Boolean) : SlidersExpandableViewModel
+
+    data object Fixed : SlidersExpandableViewModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 1568e8c0..2e29bbd 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -20,6 +20,7 @@
 import static android.app.WallpaperManager.FLAG_SYSTEM;
 import static android.app.WallpaperManager.SetWallpaperFlags;
 
+import static com.android.systemui.Flags.fixImageWallpaperCrashSurfaceAlreadyReleased;
 import static com.android.window.flags.Flags.offloadColorExtraction;
 
 import android.annotation.Nullable;
@@ -128,8 +129,17 @@
          * and if the count is 0, unload the bitmap
          */
         private int mBitmapUsages = 0;
+
+        /**
+         * Main lock for long operations (loading the bitmap or processing colors).
+         */
         private final Object mLock = new Object();
 
+        /**
+         * Lock for SurfaceHolder operations. Should only be acquired after the main lock.
+         */
+        private final Object mSurfaceLock = new Object();
+
         CanvasEngine() {
             super();
             setFixedSizeAllowed(true);
@@ -223,6 +233,12 @@
             if (DEBUG) {
                 Log.i(TAG, "onSurfaceDestroyed");
             }
+            if (fixImageWallpaperCrashSurfaceAlreadyReleased()) {
+                synchronized (mSurfaceLock) {
+                    mSurfaceHolder = null;
+                }
+                return;
+            }
             mLongExecutor.execute(this::onSurfaceDestroyedSynchronized);
         }
 
@@ -259,7 +275,7 @@
         }
 
         private void drawFrameInternal() {
-            if (mSurfaceHolder == null) {
+            if (mSurfaceHolder == null && !fixImageWallpaperCrashSurfaceAlreadyReleased()) {
                 Log.i(TAG, "attempt to draw a frame without a valid surface");
                 return;
             }
@@ -268,6 +284,19 @@
             if (!isBitmapLoaded()) {
                 loadWallpaperAndDrawFrameInternal();
             } else {
+                if (fixImageWallpaperCrashSurfaceAlreadyReleased()) {
+                    synchronized (mSurfaceLock) {
+                        if (mSurfaceHolder == null) {
+                            Log.i(TAG, "Surface released before the image could be drawn");
+                            return;
+                        }
+                        mBitmapUsages++;
+                        drawFrameOnCanvas(mBitmap);
+                        reportEngineShown(false);
+                        unloadBitmapIfNotUsedInternal();
+                        return;
+                    }
+                }
                 mBitmapUsages++;
                 drawFrameOnCanvas(mBitmap);
                 reportEngineShown(false);
@@ -328,9 +357,14 @@
                 mBitmap.recycle();
             }
             mBitmap = null;
-
-            final Surface surface = getSurfaceHolder().getSurface();
-            surface.hwuiDestroy();
+            if (fixImageWallpaperCrashSurfaceAlreadyReleased()) {
+                synchronized (mSurfaceLock) {
+                    if (mSurfaceHolder != null) mSurfaceHolder.getSurface().hwuiDestroy();
+                }
+            } else {
+                final Surface surface = getSurfaceHolder().getSurface();
+                surface.hwuiDestroy();
+            }
             mWallpaperManager.forgetLoadedWallpaper();
             Trace.endSection();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index b86a7c9..e073f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -98,7 +98,7 @@
         CoreStartable,
         CommandQueue.Callbacks {
     private static final String TAG = WMShell.class.getName();
-    private static final int INVALID_SYSUI_STATE_MASK =
+    private static final long INVALID_SYSUI_STATE_MASK =
             SYSUI_STATE_DIALOG_SHOWING
                     | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
                     | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 6f550ba..5702a8c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -21,14 +21,18 @@
 import android.view.ViewTreeObserver
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.log.core.LogLevel
@@ -68,8 +72,9 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import com.android.systemui.Flags as AConfigFlags
+import org.mockito.Mockito.`when` as whenever
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
@@ -319,26 +324,16 @@
     fun listenForDozeAmountTransition_updatesClockDozeAmount() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(
-                    keyguardTransitionInteractor.transition(
-                        KeyguardState.LOCKSCREEN,
-                        KeyguardState.AOD
-                    )
-                )
+            whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD)))
                 .thenReturn(transitionStep)
-            whenever(
-                    keyguardTransitionInteractor.transition(
-                        KeyguardState.AOD,
-                        KeyguardState.LOCKSCREEN
-                    )
-                )
+            whenever(keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN)))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForDozeAmountTransition(this)
             transitionStep.value =
                 TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
+                    from = LOCKSCREEN,
+                    to = AOD,
                     value = 0.4f,
                     transitionState = TransitionState.RUNNING,
                 )
@@ -353,14 +348,14 @@
     fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
+            whenever(keyguardTransitionInteractor.transitionStepsToState(AOD))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToAodTransition(this)
             transitionStep.value =
                 TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
+                    from = GONE,
+                    to = AOD,
                     transitionState = TransitionState.STARTED,
                 )
             yield()
@@ -374,16 +369,16 @@
     fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZero() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.LOCKSCREEN))
-                    .thenReturn(transitionStep)
+            whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN))
+                .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToLockscreenTransition(this)
             transitionStep.value =
-                    TransitionStep(
-                            from = KeyguardState.OCCLUDED,
-                            to = KeyguardState.LOCKSCREEN,
-                            transitionState = TransitionState.STARTED,
-                    )
+                TransitionStep(
+                    from = OCCLUDED,
+                    to = LOCKSCREEN,
+                    transitionState = TransitionState.STARTED,
+                )
             yield()
 
             verify(animations, times(2)).doze(0f)
@@ -395,37 +390,37 @@
     fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
+            whenever(keyguardTransitionInteractor.transitionStepsToState(AOD))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToAodTransition(this)
             transitionStep.value =
                 TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
+                    from = LOCKSCREEN,
+                    to = AOD,
                     transitionState = TransitionState.STARTED,
                 )
             yield()
 
             verify(animations, never()).doze(1f)
 
-                job.cancel()
-            }
+            job.cancel()
+        }
 
     @Test
     fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.LOCKSCREEN))
-                    .thenReturn(transitionStep)
+            whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN))
+                .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToLockscreenTransition(this)
             transitionStep.value =
-                    TransitionStep(
-                            from = KeyguardState.AOD,
-                            to = KeyguardState.LOCKSCREEN,
-                            transitionState = TransitionState.STARTED,
-                    )
+                TransitionStep(
+                    from = AOD,
+                    to = LOCKSCREEN,
+                    transitionState = TransitionState.STARTED,
+                )
             yield()
 
             verify(animations, never()).doze(0f)
@@ -437,16 +432,16 @@
     fun listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOne() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.DOZING))
-                    .thenReturn(transitionStep)
+            whenever(keyguardTransitionInteractor.transitionStepsToState(DOZING))
+                .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToDozingTransition(this)
             transitionStep.value =
-                    TransitionStep(
-                            from = KeyguardState.LOCKSCREEN,
-                            to = KeyguardState.DOZING,
-                            transitionState = TransitionState.STARTED,
-                    )
+                TransitionStep(
+                    from = LOCKSCREEN,
+                    to = DOZING,
+                    transitionState = TransitionState.STARTED,
+                )
             yield()
 
             verify(animations, times(2)).doze(1f)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 51ceda3..f9fe5e7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.when;
 
 import android.os.SystemClock;
+import android.platform.test.annotations.EnableFlags;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.KeyEvent;
@@ -125,8 +126,8 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
     public void withFeatureFlagOn_oldMessage_isHidden() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES);
         KeyguardAbsKeyInputViewController underTest = createTestObject();
 
         underTest.init();
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 6dc5b72..bbdd805 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doAnswer;
@@ -104,7 +105,7 @@
         }).when(mAccessibilityManager).setMagnificationConnection(
                 any(IMagnificationConnection.class));
 
-        when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+        when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
 
         doAnswer(invocation -> {
             mMagnification.mMagnificationSettingsControllerCallback
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index e0df1e0..2d5e3a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.graphics.PointF;
-import android.platform.test.annotations.EnableFlags;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
@@ -40,7 +39,6 @@
 import androidx.dynamicanimation.animation.SpringForce;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.Flags;
 import com.android.systemui.Prefs;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.utils.TestUtils;
@@ -230,7 +228,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK)
     public void tuck_animates() {
         mMenuAnimationController.cancelAnimations();
         mMenuAnimationController.moveToEdgeAndHide();
@@ -239,7 +236,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK)
     public void untuck_animates() {
         mMenuAnimationController.cancelAnimations();
         mMenuAnimationController.moveOutEdgeAndShow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
index bf6ca06..e371b39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
@@ -46,12 +46,12 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Mock
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
 
 private const val ON: Int = 1
 private const val OFF: Int = 0
@@ -90,7 +90,7 @@
         secureSettings = FakeSettings()
         systemClock = FakeSystemClock()
         backgroundDelayableExecutor = FakeExecutor(systemClock)
-        whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
+        whenever(sysuiState.setFlag(anyLong(), anyBoolean())).thenReturn(sysuiState)
 
         fontScalingDialogDelegate =
             spy(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index ebb6b48..8895a5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -118,7 +119,7 @@
         when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
         when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mDevices);
         when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
-        when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+        when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
         when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
         when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 8e4c155..fd37cad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -8,6 +8,7 @@
 import android.graphics.Point
 import android.graphics.Rect
 import android.os.Looper
+import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.view.IRemoteAnimationFinishedCallback
@@ -17,15 +18,20 @@
 import android.view.ViewGroup
 import android.widget.FrameLayout
 import android.widget.LinearLayout
+import android.window.RemoteTransition
+import android.window.TransitionFilter
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.Flags
 import com.android.systemui.util.mockito.any
+import com.android.wm.shell.shared.ShellTransitions
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertNotNull
 import junit.framework.Assert.assertNull
 import junit.framework.Assert.assertTrue
 import junit.framework.AssertionFailedError
 import kotlin.concurrent.thread
+import kotlin.test.assertEquals
 import org.junit.After
 import org.junit.Assert.assertThrows
 import org.junit.Before
@@ -48,6 +54,7 @@
     private val transitionContainer = LinearLayout(mContext)
     private val mainExecutor = context.mainExecutor
     private val testTransitionAnimator = fakeTransitionAnimator(mainExecutor)
+    private val testShellTransitions = FakeShellTransitions()
     @Mock lateinit var callback: ActivityTransitionAnimator.Callback
     @Mock lateinit var listener: ActivityTransitionAnimator.Listener
     @Spy private val controller = TestTransitionAnimatorController(transitionContainer)
@@ -55,12 +62,16 @@
 
     private lateinit var activityTransitionAnimator: ActivityTransitionAnimator
     @get:Rule val rule = MockitoJUnit.rule()
+    @get:Rule val setFlagsRule = SetFlagsRule()
 
     @Before
     fun setup() {
         activityTransitionAnimator =
             ActivityTransitionAnimator(
                 mainExecutor,
+                ActivityTransitionAnimator.TransitionRegister.fromShellTransitions(
+                    testShellTransitions
+                ),
                 testTransitionAnimator,
                 testTransitionAnimator,
                 disableWmTimeout = true,
@@ -164,6 +175,34 @@
     }
 
     @Test
+    fun registersReturnIffCookieIsPresent() {
+        setFlagsRule.enableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY)
+        `when`(callback.isOnKeyguard()).thenReturn(false)
+
+        startIntentWithAnimation(activityTransitionAnimator, controller) { _ ->
+            ActivityManager.START_DELIVERED_TO_TOP
+        }
+
+        waitForIdleSync()
+        assertTrue(testShellTransitions.remotes.isEmpty())
+        assertTrue(testShellTransitions.remotesForTakeover.isEmpty())
+
+        val controller =
+            object : DelegateTransitionAnimatorController(controller) {
+                override val transitionCookie
+                    get() = ActivityTransitionAnimator.TransitionCookie("testCookie")
+            }
+
+        startIntentWithAnimation(activityTransitionAnimator, controller) { _ ->
+            ActivityManager.START_DELIVERED_TO_TOP
+        }
+
+        waitForIdleSync()
+        assertEquals(1, testShellTransitions.remotes.size)
+        assertTrue(testShellTransitions.remotesForTakeover.isEmpty())
+    }
+
+    @Test
     fun doesNotStartIfAnimationIsCancelled() {
         val runner = activityTransitionAnimator.createRunner(controller)
         runner.onAnimationCancelled()
@@ -243,6 +282,35 @@
 }
 
 /**
+ * A fake implementation of [ShellTransitions] which saves filter-transition pairs locally and
+ * allows inspection.
+ */
+private class FakeShellTransitions : ShellTransitions {
+    val remotes = mutableMapOf<TransitionFilter, RemoteTransition>()
+    val remotesForTakeover = mutableMapOf<TransitionFilter, RemoteTransition>()
+
+    override fun registerRemote(filter: TransitionFilter, remoteTransition: RemoteTransition) {
+        remotes[filter] = remoteTransition
+    }
+
+    override fun registerRemoteForTakeover(
+        filter: TransitionFilter,
+        remoteTransition: RemoteTransition
+    ) {
+        remotesForTakeover[filter] = remoteTransition
+    }
+
+    override fun unregisterRemote(remoteTransition: RemoteTransition) {
+        while (remotes.containsValue(remoteTransition)) {
+            remotes.values.remove(remoteTransition)
+        }
+        while (remotesForTakeover.containsValue(remoteTransition)) {
+            remotesForTakeover.values.remove(remoteTransition)
+        }
+    }
+}
+
+/**
  * A simple implementation of [ActivityTransitionAnimator.Controller] which throws if it is called
  * outside of the main thread.
  */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
index b31fe21..42fcd54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
@@ -16,12 +16,16 @@
 
 package com.android.systemui.animation
 
+import android.os.HandlerThread
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import android.view.View
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.view.LaunchableFrameLayout
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -30,6 +34,13 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class GhostedViewTransitionAnimatorControllerTest : SysuiTestCase() {
+    companion object {
+        private const val LAUNCH_CUJ = 0
+        private const val RETURN_CUJ = 1
+    }
+
+    private val interactionJankMonitor = FakeInteractionJankMonitor()
+
     @Test
     fun animatingOrphanViewDoesNotCrash() {
         val state = TransitionAnimator.State(top = 0, bottom = 0, left = 0, right = 0)
@@ -47,4 +58,63 @@
             GhostedViewTransitionAnimatorController(FrameLayout(mContext))
         }
     }
+
+    @Test
+    fun cujsAreLoggedCorrectly() {
+        val parent = FrameLayout(mContext)
+
+        val launchView = LaunchableFrameLayout(mContext)
+        parent.addView((launchView))
+        val launchController =
+            GhostedViewTransitionAnimatorController(
+                    launchView,
+                launchCujType = LAUNCH_CUJ,
+                returnCujType = RETURN_CUJ,
+                interactionJankMonitor = interactionJankMonitor
+            )
+        launchController.onTransitionAnimationStart(isExpandingFullyAbove = true)
+        assertThat(interactionJankMonitor.ongoing).containsExactly(LAUNCH_CUJ)
+        launchController.onTransitionAnimationEnd(isExpandingFullyAbove = true)
+        assertThat(interactionJankMonitor.ongoing).isEmpty()
+        assertThat(interactionJankMonitor.finished).containsExactly(LAUNCH_CUJ)
+
+        val returnView = LaunchableFrameLayout(mContext)
+        parent.addView((returnView))
+        val returnController =
+            object : GhostedViewTransitionAnimatorController(
+                returnView,
+                launchCujType = LAUNCH_CUJ,
+                returnCujType = RETURN_CUJ,
+                interactionJankMonitor = interactionJankMonitor
+            ) {
+                override val isLaunching = false
+            }
+        returnController.onTransitionAnimationStart(isExpandingFullyAbove = true)
+        assertThat(interactionJankMonitor.ongoing).containsExactly(RETURN_CUJ)
+        returnController.onTransitionAnimationEnd(isExpandingFullyAbove = true)
+        assertThat(interactionJankMonitor.ongoing).isEmpty()
+        assertThat(interactionJankMonitor.finished).containsExactly(LAUNCH_CUJ, RETURN_CUJ)
+    }
+
+    /**
+     * A fake implementation of [InteractionJankMonitor] which stores ongoing and finished CUJs and
+     * allows inspection.
+     */
+    private class FakeInteractionJankMonitor : InteractionJankMonitor(
+        HandlerThread("testThread")
+    ) {
+        val ongoing: MutableSet<Int> = mutableSetOf()
+        val finished: MutableSet<Int> = mutableSetOf()
+
+        override fun begin(v: View?, cujType: Int): Boolean {
+            ongoing.add(cujType)
+            return true
+        }
+
+        override fun end(cujType: Int): Boolean {
+            ongoing.remove(cujType)
+            finished.add(cujType)
+            return true
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index de3b741..e81369d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -43,6 +43,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.widget.LockPatternUtils
+import com.android.launcher3.icons.IconProvider
 import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
@@ -150,6 +151,7 @@
     private lateinit var displayStateInteractor: DisplayStateInteractor
     private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
     private lateinit var biometricStatusInteractor: BiometricStatusInteractor
+    private lateinit var iconProvider: IconProvider
 
     private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
     private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
@@ -178,6 +180,7 @@
         biometricStatusInteractor =
                 BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository,
                     fingerprintRepository)
+        iconProvider = IconProvider(context)
         // Set up default logo icon
         whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon)
         context.setMockPackageManager(packageManager)
@@ -649,14 +652,15 @@
         lockPatternUtils,
         interactionJankMonitor,
         { promptSelectorInteractor },
-        { bpCredentialInteractor },
         PromptViewModel(
             displayStateInteractor,
             promptSelectorInteractor,
             context,
             udfpsOverlayInteractor,
             biometricStatusInteractor,
-            udfpsUtils
+            udfpsUtils,
+            iconProvider,
+            activityTaskManager
         ),
         { credentialViewModel },
         Handler(TestableLooper.get(this).looper),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
index df0e5a7..5e4272f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
@@ -16,13 +16,8 @@
 
 package com.android.systemui.biometrics.data.repository
 
-import android.hardware.biometrics.BiometricManager
-import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
-import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
 import android.hardware.biometrics.PromptInfo
-import android.hardware.biometrics.PromptVerticalListContentView
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.shared.model.PromptKind
@@ -139,83 +134,6 @@
         }
 
     @Test
-    fun showBpWithoutIconForCredential_withVerticalListContentView() =
-        testScope.runTest {
-            mSetFlagsRule.enableFlags(Flags.FLAG_CONSTRAINT_BP)
-            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-            for (case in
-                listOf(
-                    PromptKind.Biometric(),
-                    PromptKind.Pin,
-                    PromptKind.Password,
-                    PromptKind.Pattern
-                )) {
-                val hasCredentialViewShown = case !is PromptKind.Biometric
-                val promptInfo =
-                    PromptInfo().apply {
-                        authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
-                        contentView = PromptVerticalListContentView.Builder().build()
-                    }
-                repository.setPrompt(promptInfo, USER_ID, CHALLENGE, case, OP_PACKAGE_NAME)
-                repository.setShouldShowBpWithoutIconForCredential(promptInfo)
-
-                assertThat(repository.showBpWithoutIconForCredential.value)
-                    .isEqualTo(!hasCredentialViewShown)
-            }
-        }
-
-    @Test
-    fun showBpWithoutIconForCredential_withContentViewWithMoreOptionsButton() =
-        testScope.runTest {
-            mSetFlagsRule.enableFlags(Flags.FLAG_CONSTRAINT_BP)
-            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-            val promptInfo =
-                PromptInfo().apply {
-                    authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
-                    contentView =
-                        PromptContentViewWithMoreOptionsButton.Builder()
-                            .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
-                            .build()
-                }
-            for (case in
-                listOf(
-                    PromptKind.Biometric(),
-                    PromptKind.Pin,
-                    PromptKind.Password,
-                    PromptKind.Pattern
-                )) {
-                repository.setPrompt(promptInfo, USER_ID, CHALLENGE, case, OP_PACKAGE_NAME)
-                repository.setShouldShowBpWithoutIconForCredential(promptInfo)
-
-                assertThat(repository.showBpWithoutIconForCredential.value).isFalse()
-            }
-        }
-
-    @Test
-    fun showBpWithoutIconForCredential_withDescription() =
-        testScope.runTest {
-            mSetFlagsRule.enableFlags(Flags.FLAG_CONSTRAINT_BP)
-            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-            for (case in
-                listOf(
-                    PromptKind.Biometric(),
-                    PromptKind.Pin,
-                    PromptKind.Password,
-                    PromptKind.Pattern
-                )) {
-                val promptInfo =
-                    PromptInfo().apply {
-                        authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
-                        description = "description"
-                    }
-                repository.setPrompt(promptInfo, USER_ID, CHALLENGE, case, OP_PACKAGE_NAME)
-                repository.setShouldShowBpWithoutIconForCredential(promptInfo)
-
-                assertThat(repository.showBpWithoutIconForCredential.value).isFalse()
-            }
-        }
-
-    @Test
     fun setsAndUnsetsPrompt() =
         testScope.runTest {
             val kind = PromptKind.Pin
@@ -223,7 +141,7 @@
 
             repository.setPrompt(promptInfo, USER_ID, CHALLENGE, kind, OP_PACKAGE_NAME)
 
-            assertThat(repository.kind.value).isEqualTo(kind)
+            assertThat(repository.promptKind.value).isEqualTo(kind)
             assertThat(repository.userId.value).isEqualTo(USER_ID)
             assertThat(repository.challenge.value).isEqualTo(CHALLENGE)
             assertThat(repository.promptInfo.value).isSameInstanceAs(promptInfo)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index c308507..4068404 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -17,8 +17,11 @@
 package com.android.systemui.biometrics.domain.interactor
 
 import android.app.admin.DevicePolicyManager
+import android.content.ComponentName
 import android.hardware.biometrics.BiometricManager.Authenticators
+import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
 import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.PromptVerticalListContentView
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
@@ -29,8 +32,10 @@
 import com.android.systemui.biometrics.shared.model.BiometricModalities
 import com.android.systemui.biometrics.shared.model.PromptKind
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
@@ -43,19 +48,22 @@
 import org.mockito.Mock
 import org.mockito.junit.MockitoJUnit
 
-private const val TITLE = "hey there"
-private const val SUBTITLE = "ok"
-private const val DESCRIPTION = "football"
-private const val NEGATIVE_TEXT = "escape"
-
-private const val USER_ID = 8
-private const val CHALLENGE = 999L
-private const val OP_PACKAGE_NAME = "biometric.testapp"
-
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(JUnit4::class)
 class PromptSelectorInteractorImplTest : SysuiTestCase() {
+    companion object {
+        private const val TITLE = "hey there"
+        private const val SUBTITLE = "ok"
+        private const val DESCRIPTION = "football"
+        private const val NEGATIVE_TEXT = "escape"
+
+        private const val USER_ID = 8
+        private const val CHALLENGE = 999L
+        private const val OP_PACKAGE_NAME = "biometric.testapp"
+        private val componentNameOverriddenForConfirmDeviceCredentialActivity =
+            ComponentName("not.com.android.settings", "testapp")
+    }
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
 
@@ -64,6 +72,7 @@
     private val testScope = TestScope()
     private val fingerprintRepository = FakeFingerprintPropertyRepository()
     private val promptRepository = FakePromptRepository()
+    private val fakeExecutor = FakeExecutor(FakeSystemClock())
 
     private lateinit var interactor: PromptSelectorInteractor
 
@@ -73,6 +82,23 @@
             PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
     }
 
+    private fun basicPromptInfo() =
+        PromptInfo().apply {
+            title = TITLE
+            subtitle = SUBTITLE
+            description = DESCRIPTION
+            negativeButtonText = NEGATIVE_TEXT
+            isConfirmationRequested = true
+            isDeviceCredentialAllowed = true
+            authenticators = Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL
+        }
+
+    private val modalities =
+        BiometricModalities(
+            fingerprintProperties = fingerprintSensorPropertiesInternal().first(),
+            faceProperties = faceSensorPropertiesInternal().first(),
+        )
+
     @Test
     fun useBiometricsAndReset() =
         testScope.runTest { useBiometricsAndReset(allowCredentialFallback = true) }
@@ -81,16 +107,24 @@
     fun useBiometricsAndResetWithoutFallback() =
         testScope.runTest { useBiometricsAndReset(allowCredentialFallback = false) }
 
-    private fun TestScope.useBiometricsAndReset(allowCredentialFallback: Boolean) {
+    @Test
+    fun useBiometricsAndResetOnConfirmDeviceCredentialActivity() =
+        testScope.runTest {
+            useBiometricsAndReset(
+                allowCredentialFallback = true,
+                setComponentNameForConfirmDeviceCredentialActivity = true
+            )
+        }
+
+    private fun TestScope.useBiometricsAndReset(
+        allowCredentialFallback: Boolean,
+        setComponentNameForConfirmDeviceCredentialActivity: Boolean = false
+    ) {
         setUserCredentialType(isPassword = true)
 
         val confirmationRequired = true
         val info =
-            PromptInfo().apply {
-                title = TITLE
-                subtitle = SUBTITLE
-                description = DESCRIPTION
-                negativeButtonText = NEGATIVE_TEXT
+            basicPromptInfo().apply {
                 isConfirmationRequested = confirmationRequired
                 authenticators =
                     if (allowCredentialFallback) {
@@ -99,26 +133,27 @@
                         Authenticators.BIOMETRIC_STRONG
                     }
                 isDeviceCredentialAllowed = allowCredentialFallback
+                componentNameForConfirmDeviceCredentialActivity =
+                    if (setComponentNameForConfirmDeviceCredentialActivity)
+                        componentNameOverriddenForConfirmDeviceCredentialActivity
+                    else null
             }
-        val modalities =
-            BiometricModalities(
-                fingerprintProperties = fingerprintSensorPropertiesInternal().first(),
-                faceProperties = faceSensorPropertiesInternal().first(),
-            )
 
         val currentPrompt by collectLastValue(interactor.prompt)
-        val credentialKind by collectLastValue(interactor.credentialKind)
+        val promptKind by collectLastValue(interactor.promptKind)
         val isCredentialAllowed by collectLastValue(interactor.isCredentialAllowed)
-        val isExplicitConfirmationRequired by collectLastValue(interactor.isConfirmationRequired)
+        val credentialKind by collectLastValue(interactor.credentialKind)
+        val isConfirmationRequired by collectLastValue(interactor.isConfirmationRequired)
 
         assertThat(currentPrompt).isNull()
 
-        interactor.useBiometricsForAuthentication(
+        interactor.setPrompt(
             info,
             USER_ID,
-            CHALLENGE,
             modalities,
-            OP_PACKAGE_NAME
+            CHALLENGE,
+            OP_PACKAGE_NAME,
+            false /*onSwitchToCredential*/
         )
 
         assertThat(currentPrompt).isNotNull()
@@ -127,15 +162,22 @@
         assertThat(currentPrompt?.subtitle).isEqualTo(SUBTITLE)
         assertThat(currentPrompt?.negativeButtonText).isEqualTo(NEGATIVE_TEXT)
         assertThat(currentPrompt?.opPackageName).isEqualTo(OP_PACKAGE_NAME)
+        assertThat(promptKind!!.isBiometric()).isTrue()
+        assertThat(currentPrompt?.componentNameForConfirmDeviceCredentialActivity)
+            .isEqualTo(
+                if (setComponentNameForConfirmDeviceCredentialActivity)
+                    componentNameOverriddenForConfirmDeviceCredentialActivity
+                else null
+            )
 
         if (allowCredentialFallback) {
             assertThat(credentialKind).isSameInstanceAs(PromptKind.Password)
             assertThat(isCredentialAllowed).isTrue()
         } else {
-            assertThat(credentialKind).isEqualTo(PromptKind.Biometric())
+            assertThat(credentialKind).isEqualTo(PromptKind.None)
             assertThat(isCredentialAllowed).isFalse()
         }
-        assertThat(isExplicitConfirmationRequired).isEqualTo(confirmationRequired)
+        assertThat(isConfirmationRequired).isEqualTo(confirmationRequired)
 
         interactor.resetPrompt()
         verifyUnset()
@@ -152,6 +194,143 @@
     fun usePasswordCredentialAndReset() =
         testScope.runTest { useCredentialAndReset(PromptKind.Password) }
 
+    @Test
+    fun promptKind_isBiometric_whenBiometricAllowed() =
+        testScope.runTest {
+            setUserCredentialType(isPassword = true)
+            val info = basicPromptInfo()
+
+            val promptKind by collectLastValue(interactor.promptKind)
+            assertThat(promptKind).isEqualTo(PromptKind.None)
+
+            interactor.setPrompt(
+                info,
+                USER_ID,
+                modalities,
+                CHALLENGE,
+                OP_PACKAGE_NAME,
+                false /*onSwitchToCredential*/
+            )
+
+            assertThat(promptKind?.isBiometric()).isTrue()
+
+            interactor.resetPrompt()
+            verifyUnset()
+        }
+
+    @Test
+    fun promptKind_isCredential_onSwitchToCredential() =
+        testScope.runTest {
+            setUserCredentialType(isPassword = true)
+            val info = basicPromptInfo()
+
+            val promptKind by collectLastValue(interactor.promptKind)
+            assertThat(promptKind).isEqualTo(PromptKind.None)
+
+            interactor.setPrompt(
+                info,
+                USER_ID,
+                modalities,
+                CHALLENGE,
+                OP_PACKAGE_NAME,
+                true /*onSwitchToCredential*/
+            )
+
+            assertThat(promptKind).isEqualTo(PromptKind.Password)
+
+            interactor.resetPrompt()
+            verifyUnset()
+        }
+
+    @Test
+    fun promptKind_isCredential_whenBiometricIsNotAllowed() =
+        testScope.runTest {
+            setUserCredentialType(isPassword = true)
+            val info =
+                basicPromptInfo().apply {
+                    isDeviceCredentialAllowed = true
+                    authenticators = Authenticators.DEVICE_CREDENTIAL
+                }
+
+            val promptKind by collectLastValue(interactor.promptKind)
+            assertThat(promptKind).isEqualTo(PromptKind.None)
+
+            interactor.setPrompt(
+                info,
+                USER_ID,
+                modalities,
+                CHALLENGE,
+                OP_PACKAGE_NAME,
+                false /*onSwitchToCredential*/
+            )
+
+            assertThat(promptKind).isEqualTo(PromptKind.Password)
+
+            interactor.resetPrompt()
+            verifyUnset()
+        }
+
+    @Test
+    fun promptKind_isCredential_whenBiometricIsNotAllowed_withMoreOptionsButton() =
+        testScope.runTest {
+            setUserCredentialType(isPassword = true)
+            val info =
+                basicPromptInfo().apply {
+                    isDeviceCredentialAllowed = true
+                    authenticators = Authenticators.DEVICE_CREDENTIAL
+                    contentView =
+                        PromptContentViewWithMoreOptionsButton.Builder()
+                            .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
+                            .build()
+                }
+
+            val promptKind by collectLastValue(interactor.promptKind)
+            assertThat(promptKind).isEqualTo(PromptKind.None)
+
+            interactor.setPrompt(
+                info,
+                USER_ID,
+                modalities,
+                CHALLENGE,
+                OP_PACKAGE_NAME,
+                false /*onSwitchToCredential*/
+            )
+
+            assertThat(promptKind).isEqualTo(PromptKind.Password)
+
+            interactor.resetPrompt()
+            verifyUnset()
+        }
+
+    @Test
+    fun promptKind_isBiometric_whenBiometricIsNotAllowed_withVerticalList() =
+        testScope.runTest {
+            setUserCredentialType(isPassword = true)
+            val info =
+                basicPromptInfo().apply {
+                    isDeviceCredentialAllowed = true
+                    authenticators = Authenticators.DEVICE_CREDENTIAL
+                    contentView = PromptVerticalListContentView.Builder().build()
+                }
+
+            val promptKind by collectLastValue(interactor.promptKind)
+            assertThat(promptKind).isEqualTo(PromptKind.None)
+
+            interactor.setPrompt(
+                info,
+                USER_ID,
+                modalities,
+                CHALLENGE,
+                OP_PACKAGE_NAME,
+                false /*onSwitchToCredential*/
+            )
+
+            assertThat(promptKind?.isBiometric()).isTrue()
+
+            interactor.resetPrompt()
+            verifyUnset()
+        }
+
     private fun TestScope.useCredentialAndReset(kind: PromptKind) {
         setUserCredentialType(
             isPin = kind == PromptKind.Pin,
@@ -173,11 +352,18 @@
 
         assertThat(currentPrompt).isNull()
 
-        interactor.useCredentialsForAuthentication(info, kind, USER_ID, CHALLENGE, OP_PACKAGE_NAME)
+        interactor.setPrompt(
+            info,
+            USER_ID,
+            BiometricModalities(),
+            CHALLENGE,
+            OP_PACKAGE_NAME,
+            false /*onSwitchToCredential*/
+        )
 
         // not using biometrics, should be null with no fallback option
         assertThat(currentPrompt).isNull()
-        assertThat(credentialKind).isEqualTo(PromptKind.Biometric())
+        assertThat(credentialKind).isEqualTo(PromptKind.None)
 
         interactor.resetPrompt()
         verifyUnset()
@@ -185,13 +371,16 @@
 
     private fun TestScope.verifyUnset() {
         val currentPrompt by collectLastValue(interactor.prompt)
+        val promptKind by collectLastValue(interactor.promptKind)
+        val isCredentialAllowed by collectLastValue(interactor.isCredentialAllowed)
         val credentialKind by collectLastValue(interactor.credentialKind)
+        val isConfirmationRequired by collectLastValue(interactor.isConfirmationRequired)
 
         assertThat(currentPrompt).isNull()
-
-        val kind = credentialKind as? PromptKind.Biometric
-        assertThat(kind).isNotNull()
-        assertThat(kind?.activeModalities?.isEmpty).isTrue()
+        assertThat(promptKind).isEqualTo(PromptKind.None)
+        assertThat(isCredentialAllowed).isFalse()
+        assertThat(credentialKind).isEqualTo(PromptKind.None)
+        assertThat(isConfirmationRequired).isFalse()
     }
 
     private fun setUserCredentialType(isPin: Boolean = false, isPassword: Boolean = false) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index a732418..fa78f0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -16,9 +16,13 @@
 
 package com.android.systemui.biometrics.ui.viewmodel
 
+import android.app.ActivityManager.RunningTaskInfo
 import android.app.ActivityTaskManager
+import android.content.ComponentName
+import android.content.pm.ActivityInfo
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
 import android.content.res.Configuration
 import android.graphics.Bitmap
 import android.graphics.Point
@@ -33,10 +37,12 @@
 import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.fingerprint.FingerprintSensorProperties
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.platform.test.annotations.EnableFlags
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
+import com.android.launcher3.icons.IconProvider
 import com.android.systemui.Flags.FLAG_BP_TALKBACK
 import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
 import com.android.systemui.SysuiTestCase
@@ -94,6 +100,7 @@
 private const val DELAY = 1000L
 private const val OP_PACKAGE_NAME = "biometric.testapp"
 private const val OP_PACKAGE_NAME_NO_ICON = "biometric.testapp.noicon"
+private const val OP_PACKAGE_NAME_CAN_NOT_BE_FOUND = "can.not.be.found"
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -107,18 +114,23 @@
     @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
     @Mock private lateinit var udfpsUtils: UdfpsUtils
     @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var iconProvider: IconProvider
     @Mock private lateinit var applicationInfoWithIcon: ApplicationInfo
     @Mock private lateinit var applicationInfoNoIcon: ApplicationInfo
     @Mock private lateinit var activityTaskManager: ActivityTaskManager
+    @Mock private lateinit var activityInfo: ActivityInfo
+    @Mock private lateinit var runningTaskInfo: RunningTaskInfo
 
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
     private val testScope = TestScope()
     private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
+    private val defaultLogoIconWithOverrides = context.getDrawable(R.drawable.ic_add)
     private val logoResFromApp = R.drawable.ic_cake
     private val logoFromApp = context.getDrawable(logoResFromApp)
     private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565)
     private val defaultLogoDescription = "Test Android App"
     private val logoDescriptionFromApp = "Test Cake App"
+    private val packageNameForLogoWithOverrides = "should.use.overridden.logo"
 
     private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
     private lateinit var promptRepository: FakePromptRepository
@@ -169,11 +181,12 @@
             )
         biometricStatusRepository = FakeBiometricStatusRepository()
         biometricStatusInteractor =
-            BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository,
-                fingerprintRepository)
-        selector =
-            PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
-        selector.resetPrompt()
+            BiometricStatusInteractorImpl(
+                activityTaskManager,
+                biometricStatusRepository,
+                fingerprintRepository
+            )
+
         promptContentView =
             PromptVerticalListContentView.Builder()
                 .addListItem(PromptContentItemBulletedText("content item 1"))
@@ -183,32 +196,35 @@
         promptContentViewWithMoreOptionsButton =
             PromptContentViewWithMoreOptionsButton.Builder()
                 .setDescription("test")
-                .setMoreOptionsButtonListener(fakeExecutor, { _, _ -> })
+                .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
                 .build()
 
-        viewModel =
-            PromptViewModel(
-                displayStateInteractor,
-                selector,
-                mContext,
-                udfpsOverlayInteractor,
-                biometricStatusInteractor,
-                udfpsUtils
-            )
-        iconViewModel = viewModel.iconViewModel
-
-        // Set up default logo icon and app customized icon
+        // Set up default logo info and app customized info
         whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_ICON), anyInt()))
             .thenReturn(applicationInfoNoIcon)
         whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME), anyInt()))
             .thenReturn(applicationInfoWithIcon)
+        whenever(packageManager.getApplicationInfo(eq(packageNameForLogoWithOverrides), anyInt()))
+            .thenReturn(applicationInfoWithIcon)
+        whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND), anyInt()))
+            .thenThrow(NameNotFoundException())
+
+        whenever(packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo)
+        whenever(iconProvider.getIcon(activityInfo)).thenReturn(defaultLogoIconWithOverrides)
         whenever(packageManager.getApplicationIcon(applicationInfoWithIcon))
             .thenReturn(defaultLogoIcon)
         whenever(packageManager.getApplicationLabel(applicationInfoWithIcon))
             .thenReturn(defaultLogoDescription)
+        whenever(packageManager.getUserBadgedIcon(any(), any())).then { it.getArgument(0) }
+        whenever(packageManager.getUserBadgedLabel(any(), any())).then { it.getArgument(0) }
+
         context.setMockPackageManager(packageManager)
         val resources = context.getOrCreateTestableResources()
         resources.addOverride(logoResFromApp, logoFromApp)
+        resources.addOverride(
+            R.array.biometric_dialog_package_names_for_logo_with_overrides,
+            arrayOf(packageNameForLogoWithOverrides)
+        )
     }
 
     @Test
@@ -1257,8 +1273,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_BP_TALKBACK)
     fun hint_for_talkback_guidance() = runGenericTest {
-        mSetFlagsRule.enableFlags(FLAG_BP_TALKBACK)
         val hint by collectLastValue(viewModel.accessibilityHint)
 
         // Touches should fall outside of sensor area
@@ -1280,10 +1296,9 @@
     }
 
     @Test
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun descriptionOverriddenByVerticalListContentView() =
         runGenericTest(contentView = promptContentView, description = "test description") {
-            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-            mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
             val contentView by collectLastValue(viewModel.contentView)
             val description by collectLastValue(viewModel.description)
 
@@ -1292,13 +1307,12 @@
         }
 
     @Test
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun descriptionOverriddenByContentViewWithMoreOptionsButton() =
         runGenericTest(
             contentView = promptContentViewWithMoreOptionsButton,
             description = "test description"
         ) {
-            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-            mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
             val contentView by collectLastValue(viewModel.contentView)
             val description by collectLastValue(viewModel.description)
 
@@ -1307,10 +1321,9 @@
         }
 
     @Test
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun descriptionWithoutContentView() =
         runGenericTest(description = "test description") {
-            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-            mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
             val contentView by collectLastValue(viewModel.contentView)
             val description by collectLastValue(viewModel.description)
 
@@ -1319,62 +1332,84 @@
         }
 
     @Test
-    fun logoIsNullIfPackageNameNotFound() =
-        runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
-            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-            mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    fun logo_nullIfPkgNameNotFound() =
+        runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
             val logo by collectLastValue(viewModel.logo)
             assertThat(logo).isNull()
         }
 
     @Test
-    fun defaultLogoIfNoLogoSet() = runGenericTest {
-        mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-        mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    fun logo_defaultWithOverrides() =
+        runGenericTest(packageName = packageNameForLogoWithOverrides) {
+            val logo by collectLastValue(viewModel.logo)
+
+            // 1. PM.getApplicationInfo(packageNameForLogoWithOverrides) is set to return
+            // applicationInfoWithIcon with defaultLogoIcon,
+            // 2. iconProvider.getIcon() is set to return defaultLogoIconForGMSCore
+            // For the apps with packageNameForLogoWithOverrides, 2 should be called instead of 1
+            assertThat(logo).isEqualTo(defaultLogoIconWithOverrides)
+        }
+
+    @Test
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    fun logo_defaultIsNull() =
+        runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
+            val logo by collectLastValue(viewModel.logo)
+            assertThat(logo).isNull()
+        }
+
+    @Test
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    fun logo_default() = runGenericTest {
         val logo by collectLastValue(viewModel.logo)
         assertThat(logo).isEqualTo(defaultLogoIcon)
     }
 
     @Test
-    fun logoResSetByApp() =
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    fun logo_resSetByApp() =
         runGenericTest(logoRes = logoResFromApp) {
-            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-            mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
             val logo by collectLastValue(viewModel.logo)
             assertThat(logo).isEqualTo(logoFromApp)
         }
 
     @Test
-    fun logoBitmapSetByApp() =
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    fun logo_bitmapSetByApp() =
         runGenericTest(logoBitmap = logoBitmapFromApp) {
-            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-            mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
             val logo by collectLastValue(viewModel.logo)
             assertThat((logo as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp)
         }
 
     @Test
-    fun logoDescriptionIsEmptyIfPackageNameNotFound() =
-        runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
-            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-            mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    fun logoDescription_emptyIfPkgNameNotFound() =
+        runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
             val logoDescription by collectLastValue(viewModel.logoDescription)
             assertThat(logoDescription).isEqualTo("")
         }
 
     @Test
-    fun defaultLogoDescriptionIfNoLogoDescriptionSet() = runGenericTest {
-        mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-        mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    fun logoDescription_defaultIsEmpty() =
+        runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
+            val logoDescription by collectLastValue(viewModel.logoDescription)
+            assertThat(logoDescription).isEqualTo("")
+        }
+
+    @Test
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    fun logoDescription_default() = runGenericTest {
         val logoDescription by collectLastValue(viewModel.logoDescription)
         assertThat(logoDescription).isEqualTo(defaultLogoDescription)
     }
 
     @Test
-    fun logoDescriptionSetByApp() =
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    fun logoDescription_setByApp() =
         runGenericTest(logoDescription = logoDescriptionFromApp) {
-            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-            mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
             val logoDescription by collectLastValue(viewModel.logoDescription)
             assertThat(logoDescription).isEqualTo(logoDescriptionFromApp)
         }
@@ -1417,6 +1452,26 @@
         packageName: String = OP_PACKAGE_NAME,
         block: suspend TestScope.() -> Unit,
     ) {
+        val topActivity = ComponentName(packageName, "test app")
+        runningTaskInfo.topActivity = topActivity
+        whenever(activityTaskManager.getTasks(1)).thenReturn(listOf(runningTaskInfo))
+        selector =
+            PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
+        selector.resetPrompt()
+
+        viewModel =
+            PromptViewModel(
+                displayStateInteractor,
+                selector,
+                mContext,
+                udfpsOverlayInteractor,
+                biometricStatusInteractor,
+                udfpsUtils,
+                iconProvider,
+                activityTaskManager
+            )
+        iconViewModel = viewModel.iconViewModel
+
         selector.initializePrompt(
             requireConfirmation = testCase.confirmationRequested,
             allowCredentialFallback = allowCredentialFallback,
@@ -1630,12 +1685,13 @@
             isConfirmationRequested = requireConfirmation
         }
 
-    useBiometricsForAuthentication(
+    setPrompt(
         info,
         USER_ID,
-        CHALLENGE,
         BiometricModalities(fingerprintProperties = fingerprint, faceProperties = face),
+        CHALLENGE,
         packageName,
+        false /*onUseDeviceCredential*/
     )
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
index a569cee..49f2043 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
@@ -21,7 +21,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
@@ -92,7 +92,7 @@
         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
         when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
 
-        when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+        when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
         when(mSystemUIDialogFactory.create(any(), any())).thenReturn(mDialog);
 
         mBroadcastDialogDelegate = new BroadcastDialogDelegate(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
index 62c98b0..7215619 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
@@ -50,7 +50,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
@@ -104,7 +104,7 @@
         dispatcher = UnconfinedTestDispatcher(scheduler)
         testScope = TestScope(dispatcher)
 
-        whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
+        whenever(sysuiState.setFlag(anyLong(), anyBoolean())).thenReturn(sysuiState)
 
         mBluetoothTileDialogDelegate =
             BluetoothTileDialogDelegate(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
index f62a55d..11f74c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.bluetooth.qsdialog
 
 import android.bluetooth.BluetoothAdapter
+import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
@@ -63,6 +64,7 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
+@EnableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE)
 class BluetoothTileDialogViewModelTest : SysuiTestCase() {
 
     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
@@ -113,7 +115,6 @@
 
     @Before
     fun setUp() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE)
         scheduler = TestCoroutineScheduler()
         dispatcher = UnconfinedTestDispatcher(scheduler)
         testScope = TestScope(dispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index bc6c459..5361cef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -35,9 +35,13 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.flags.DisableSceneContainer;
+import com.android.systemui.flags.EnableSceneContainer;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -50,6 +54,7 @@
 import com.android.systemui.util.sensors.ThresholdSensor;
 import com.android.systemui.util.time.FakeSystemClock;
 
+import kotlinx.coroutines.flow.MutableStateFlow;
 import kotlinx.coroutines.flow.StateFlowKt;
 
 import org.junit.Before;
@@ -89,6 +94,14 @@
     private SelectedUserInteractor mSelectedUserInteractor;
     @Mock
     private CommunalInteractor mCommunalInteractor;
+    @Mock
+    private DeviceEntryInteractor mDeviceEntryInteractor;
+    private final MutableStateFlow<Boolean> mIsDeviceEntered =
+            StateFlowKt.MutableStateFlow(false);
+    @Mock
+    private SceneContainerOcclusionInteractor mSceneContainerOcclusionInteractor;
+    private final MutableStateFlow<Boolean> mIsInvisibleDueToOcclusion =
+            StateFlowKt.MutableStateFlow(false);
     private final DockManagerFake mDockManager = new DockManagerFake();
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@@ -99,15 +112,21 @@
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
         when(mShadeInteractor.isQsExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
 
+        when(mDeviceEntryInteractor.isDeviceEntered()).thenReturn(mIsDeviceEntered);
+        when(mSceneContainerOcclusionInteractor.getInvisibleDueToOcclusion()).thenReturn(
+                mIsInvisibleDueToOcclusion);
+
         mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager,
                 mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
                 mStatusBarStateController, mKeyguardStateController,
                 () -> mShadeInteractor, mBatteryController,
                 mDockManager, mFakeExecutor,
                 mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor,
-                () -> mCommunalInteractor
+                () -> mCommunalInteractor, () -> mDeviceEntryInteractor,
+                () -> mSceneContainerOcclusionInteractor
         );
         mFalsingCollector.init();
     }
@@ -189,7 +208,8 @@
     }
 
     @Test
-    public void testRegisterSensor_OccludingActivity() {
+    @DisableSceneContainer
+    public void testRegisterSensor_OccludingActivity_sceneContainerDisabled() {
         when(mKeyguardStateController.isOccluded()).thenReturn(true);
 
         ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
@@ -203,6 +223,21 @@
     }
 
     @Test
+    @EnableSceneContainer
+    public void testRegisterSensor_OccludingActivity_sceneContainerEnabled() {
+        mIsInvisibleDueToOcclusion.setValue(true);
+
+        ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
+                ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
+        verify(mStatusBarStateController).addCallback(stateListenerArgumentCaptor.capture());
+
+        mFalsingCollector.onScreenTurningOn();
+        reset(mProximitySensor);
+        stateListenerArgumentCaptor.getValue().onStateChanged(StatusBarState.SHADE);
+        verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
+    }
+
+    @Test
     public void testPassThroughEnterKeyEvent() {
         KeyEvent enterDown = KeyEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER,
                 0, 0, 0, 0, 0, 0, 0, "");
@@ -280,7 +315,8 @@
     }
 
     @Test
-    public void testAvoidUnlocked() {
+    @DisableSceneContainer
+    public void testAvoidUnlocked_sceneContainerDisabled() {
         MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
         MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
 
@@ -296,6 +332,23 @@
     }
 
     @Test
+    @EnableSceneContainer
+    public void testAvoidUnlocked_sceneContainerEnabled() {
+        MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+
+        mIsDeviceEntered.setValue(true);
+
+        // Nothing passed initially
+        mFalsingCollector.onTouchEvent(down);
+        verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+
+        // Up event would normally flush the up event, but doesn't.
+        mFalsingCollector.onTouchEvent(up);
+        verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+    }
+
+    @Test
     public void testGestureWhenDozing() {
         // We check the FalsingManager for taps during the transition to AoD (dozing=true,
         // pulsing=false), so the FalsingCollector needs to continue to analyze events that occur
diff --git a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt
deleted file mode 100644
index ab03465..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.contrast
-
-import android.app.UiModeManager
-import android.app.UiModeManager.ContrastUtils.fromContrastLevel
-import android.os.Looper
-import android.provider.Settings
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper.RunWithLooper
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.FrameLayout
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogTransitionAnimator
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.model.SysUiState
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.time.FakeSystemClock
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mock
-import org.mockito.Mockito.eq
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-/** Test the behaviour of buttons of the [ContrastDialogDelegate]. */
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@RunWithLooper
-class ContrastDialogDelegateTest : SysuiTestCase() {
-
-    private val mainExecutor = FakeExecutor(FakeSystemClock())
-    private lateinit var mContrastDialogDelegate: ContrastDialogDelegate
-    @Mock private lateinit var sysuiDialogFactory: SystemUIDialog.Factory
-    @Mock private lateinit var sysuiDialog: SystemUIDialog
-    @Mock private lateinit var mockUiModeManager: UiModeManager
-    @Mock private lateinit var mockUserTracker: UserTracker
-    @Mock private lateinit var mockSecureSettings: SecureSettings
-    @Mock private lateinit var sysuiState: SysUiState
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        mDependency.injectTestDependency(FeatureFlags::class.java, FakeFeatureFlags())
-        mDependency.injectTestDependency(SysUiState::class.java, sysuiState)
-        mDependency.injectMockDependency(DialogTransitionAnimator::class.java)
-        whenever(sysuiState.setFlag(any(), any())).thenReturn(sysuiState)
-        whenever(sysuiDialogFactory.create(any(SystemUIDialog.Delegate::class.java)))
-            .thenReturn(sysuiDialog)
-        whenever(sysuiDialog.layoutInflater).thenReturn(LayoutInflater.from(mContext))
-
-        whenever(mockUserTracker.userId).thenReturn(context.userId)
-        if (Looper.myLooper() == null) Looper.prepare()
-
-        mContrastDialogDelegate =
-            ContrastDialogDelegate(
-                sysuiDialogFactory,
-                mainExecutor,
-                mockUiModeManager,
-                mockUserTracker,
-                mockSecureSettings
-            )
-
-        mContrastDialogDelegate.createDialog()
-        val viewCaptor = ArgumentCaptor.forClass(View::class.java)
-        verify(sysuiDialog).setView(viewCaptor.capture())
-        whenever(sysuiDialog.requireViewById(anyInt()) as View?).then {
-            viewCaptor.value.requireViewById(it.getArgument(0))
-        }
-    }
-
-    @Test
-    fun testClickButtons_putsContrastInSettings() {
-        mContrastDialogDelegate.onCreate(sysuiDialog, null)
-
-        mContrastDialogDelegate.contrastButtons.forEach {
-            (contrastLevel: Int, clickedButton: FrameLayout) ->
-            clickedButton.performClick()
-            mainExecutor.runAllReady()
-            verify(mockSecureSettings)
-                .putFloatForUser(
-                    eq(Settings.Secure.CONTRAST_LEVEL),
-                    eq(fromContrastLevel(contrastLevel)),
-                    eq(context.userId)
-                )
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 8653308..44a8904 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -26,6 +26,8 @@
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.model.sysUiState
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -47,7 +49,7 @@
 
     private val testScope = kosmos.testScope
     private val testHelper = kosmos.shortcutHelperTestHelper
-
+    private val sysUiState = kosmos.sysUiState
     private val viewModel = kosmos.shortcutHelperViewModel
 
     @Test
@@ -90,12 +92,12 @@
         }
 
     @Test
-    fun shouldShow_falseAfterViewDestroyed() =
+    fun shouldShow_falseAfterViewClosed() =
         testScope.runTest {
             val shouldShow by collectLastValue(viewModel.shouldShow)
 
             testHelper.toggle(deviceId = 567)
-            viewModel.onUserLeave()
+            viewModel.onViewClosed()
 
             assertThat(shouldShow).isFalse()
         }
@@ -108,7 +110,7 @@
             testHelper.hideForSystem()
             testHelper.toggle(deviceId = 987)
             testHelper.showFromActivity()
-            viewModel.onUserLeave()
+            viewModel.onViewClosed()
             testHelper.hideFromActivity()
             testHelper.hideForSystem()
             testHelper.toggle(deviceId = 456)
@@ -127,4 +129,27 @@
             val shouldShowNew by collectLastValue(viewModel.shouldShow)
             assertThat(shouldShowNew).isEqualTo(shouldShow)
         }
+
+    @Test
+    fun sysUiStateFlag_disabledByDefault() =
+        testScope.runTest {
+            assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isFalse()
+        }
+
+    @Test
+    fun sysUiStateFlag_trueAfterViewOpened() =
+        testScope.runTest {
+            viewModel.onViewOpened()
+
+            assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isTrue()
+        }
+
+    @Test
+    fun sysUiStateFlag_falseAfterViewClosed() =
+        testScope.runTest {
+            viewModel.onViewOpened()
+            viewModel.onViewClosed()
+
+            assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isFalse()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index b50d248..977116e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -8,6 +8,8 @@
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -19,6 +21,10 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.utils.GlobalWindowManager
@@ -69,12 +75,13 @@
         resourceTrimmer =
             ResourceTrimmer(
                 keyguardInteractor,
-                powerInteractor,
-                kosmos.keyguardTransitionInteractor,
-                globalWindowManager,
-                testScope.backgroundScope,
-                kosmos.testDispatcher,
-                featureFlags
+                powerInteractor = powerInteractor,
+                keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
+                globalWindowManager = globalWindowManager,
+                applicationScope = testScope.backgroundScope,
+                bgDispatcher = kosmos.testDispatcher,
+                featureFlags = featureFlags,
+                sceneInteractor = kosmos.sceneInteractor,
             )
         resourceTrimmer.start()
     }
@@ -203,6 +210,7 @@
 
     @Test
     @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+    @DisableSceneContainer
     fun keyguardTransitionsToGone_trimsFontCache() =
         testScope.runTest {
             keyguardTransitionRepository.sendTransitionSteps(
@@ -218,6 +226,20 @@
 
     @Test
     @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+    @EnableSceneContainer
+    fun keyguardTransitionsToGone_trimsFontCache_scene_container() =
+        testScope.runTest {
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+            verify(globalWindowManager, times(1))
+                .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
+            verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_FONT)
+            verifyNoMoreInteractions(globalWindowManager)
+        }
+
+    @Test
+    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+    @DisableSceneContainer
     fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache() =
         testScope.runTest {
             featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
@@ -231,4 +253,18 @@
                 .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
             verify(globalWindowManager, times(0)).trimCaches(any())
         }
+
+    @Test
+    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+    @EnableSceneContainer
+    fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache_scene_container() =
+        testScope.runTest {
+            featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+            // Memory hidden should still be called.
+            verify(globalWindowManager, times(1))
+                .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
+            verify(globalWindowManager, times(0)).trimCaches(any())
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index 62855d7..974e3bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -21,12 +21,18 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.DismissAction
 import com.android.systemui.keyguard.shared.model.KeyguardDone
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -65,10 +71,11 @@
 
         underTest =
             KeyguardDismissActionInteractor(
-                keyguardRepository,
-                kosmos.keyguardTransitionInteractor,
-                dismissInteractorWithDependencies.interactor,
-                testScope.backgroundScope,
+                repository = keyguardRepository,
+                transitionInteractor = kosmos.keyguardTransitionInteractor,
+                dismissInteractor = dismissInteractorWithDependencies.interactor,
+                applicationScope = testScope.backgroundScope,
+                sceneInteractor = kosmos.sceneInteractor,
             )
     }
 
@@ -153,6 +160,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun executeDismissAction_dismissKeyguardRequestWithoutImmediateDismissAction() =
         testScope.runTest {
             val executeDismissAction by collectLastValue(underTest.executeDismissAction)
@@ -179,6 +187,29 @@
         }
 
     @Test
+    @EnableSceneContainer
+    fun executeDismissAction_dismissKeyguardRequestWithoutImmediateDismissAction_scene_container() =
+        testScope.runTest {
+            val executeDismissAction by collectLastValue(underTest.executeDismissAction)
+
+            // WHEN a keyguard action will run after the keyguard is gone
+            val onDismissAction = {}
+            keyguardRepository.setDismissAction(
+                DismissAction.RunAfterKeyguardGone(
+                    dismissAction = onDismissAction,
+                    onCancelAction = {},
+                    message = "message",
+                    willAnimateOnLockscreen = true,
+                )
+            )
+            assertThat(executeDismissAction).isNull()
+
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+            assertThat(executeDismissAction).isNotNull()
+        }
+
+    @Test
     fun resetDismissAction() =
         testScope.runTest {
             val resetDismissAction by collectLastValue(underTest.resetDismissAction)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index c3a806b..00f94a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dock.fakeDockManager
 import com.android.systemui.flags.BrokenWithSceneContainer
+import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -90,33 +91,40 @@
         }
     private val testScope = kosmos.testScope
 
-    private val keyguardRepository = kosmos.fakeKeyguardRepository
-    private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
+    private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+    private val bouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
     private var commandQueue = kosmos.fakeCommandQueue
     private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
-    private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val transitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
     private lateinit var featureFlags: FakeFeatureFlags
 
     // Used to verify transition requests for test output
     @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
 
-    private val fromLockscreenTransitionInteractor = kosmos.fromLockscreenTransitionInteractor
-    private val fromDreamingTransitionInteractor = kosmos.fromDreamingTransitionInteractor
-    private val fromDozingTransitionInteractor = kosmos.fromDozingTransitionInteractor
-    private val fromOccludedTransitionInteractor = kosmos.fromOccludedTransitionInteractor
-    private val fromGoneTransitionInteractor = kosmos.fromGoneTransitionInteractor
-    private val fromAodTransitionInteractor = kosmos.fromAodTransitionInteractor
-    private val fromAlternateBouncerTransitionInteractor =
+    private val fromLockscreenTransitionInteractor by lazy {
+        kosmos.fromLockscreenTransitionInteractor
+    }
+    private val fromDreamingTransitionInteractor by lazy { kosmos.fromDreamingTransitionInteractor }
+    private val fromDozingTransitionInteractor by lazy { kosmos.fromDozingTransitionInteractor }
+    private val fromOccludedTransitionInteractor by lazy { kosmos.fromOccludedTransitionInteractor }
+    private val fromGoneTransitionInteractor by lazy { kosmos.fromGoneTransitionInteractor }
+    private val fromAodTransitionInteractor by lazy { kosmos.fromAodTransitionInteractor }
+    private val fromAlternateBouncerTransitionInteractor by lazy {
         kosmos.fromAlternateBouncerTransitionInteractor
-    private val fromPrimaryBouncerTransitionInteractor =
+    }
+    private val fromPrimaryBouncerTransitionInteractor by lazy {
         kosmos.fromPrimaryBouncerTransitionInteractor
-    private val fromDreamingLockscreenHostedTransitionInteractor =
+    }
+    private val fromDreamingLockscreenHostedTransitionInteractor by lazy {
         kosmos.fromDreamingLockscreenHostedTransitionInteractor
-    private val fromGlanceableHubTransitionInteractor = kosmos.fromGlanceableHubTransitionInteractor
+    }
+    private val fromGlanceableHubTransitionInteractor by lazy {
+        kosmos.fromGlanceableHubTransitionInteractor
+    }
 
-    private val powerInteractor = kosmos.powerInteractor
-    private val communalInteractor = kosmos.communalInteractor
-    private val dockManager = kosmos.fakeDockManager
+    private val powerInteractor by lazy { kosmos.powerInteractor }
+    private val communalInteractor by lazy { kosmos.communalInteractor }
+    private val dockManager by lazy { kosmos.fakeDockManager }
 
     companion object {
         @JvmStatic
@@ -157,6 +165,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun lockscreenToPrimaryBouncerViaBouncerShowingCall() =
         testScope.runTest {
             // GIVEN a prior transition has run to LOCKSCREEN
@@ -370,6 +379,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun dreamingLockscreenHostedToGone() =
         testScope.runTest {
             // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
@@ -396,6 +406,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun dreamingLockscreenHostedToPrimaryBouncer() =
         testScope.runTest {
             // GIVEN a device dreaming with lockscreen hosted dream and not dozing
@@ -623,12 +634,13 @@
 
     /** This handles security method NONE and screen off with lock timeout */
     @Test
+    @DisableSceneContainer
     fun dreamingToGoneWithKeyguardNotShowing() =
         testScope.runTest {
             // GIVEN a prior transition has run to DREAMING
             keyguardRepository.setDreamingWithOverlay(true)
             runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
-            runCurrent()
+            advanceTimeBy(60L)
 
             // WHEN the device wakes up without a keyguard
             keyguardRepository.setKeyguardShowing(false)
@@ -679,6 +691,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun goneToDozing() =
         testScope.runTest {
             // GIVEN a device with AOD not available
@@ -704,6 +717,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun goneToAod() =
         testScope.runTest {
             // GIVEN a device with AOD available
@@ -751,6 +765,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun goneToDreaming() =
         testScope.runTest {
             // GIVEN a device that is not dreaming or dozing
@@ -809,6 +824,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun alternateBouncerToPrimaryBouncer() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
@@ -985,6 +1001,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun primaryBouncerToAod() =
         testScope.runTest {
             // GIVEN aod available
@@ -1015,6 +1032,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun primaryBouncerToDozing() =
         testScope.runTest {
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
@@ -1043,6 +1061,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun primaryBouncerToLockscreen() =
         testScope.runTest {
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
@@ -1066,6 +1085,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun primaryBouncerToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
@@ -1097,6 +1117,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun primaryBouncerToGlanceableHubWhileDreaming() =
         testScope.runTest {
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
@@ -1132,6 +1153,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun primaryBouncerToDreamingLockscreenHosted() =
         testScope.runTest {
             // GIVEN device dreaming with the lockscreen hosted dream and not dozing
@@ -1445,7 +1467,7 @@
             keyguardRepository.setKeyguardOccluded(false)
             runCurrent()
 
-            // THEN a transition to GLANCEABLE_HUB should occur
+            // THEN a transition to DREAMING should occur
             assertThat(transitionRepository)
                 .startedTransition(
                     ownerName = FromOccludedTransitionInteractor::class.simpleName,
@@ -1511,6 +1533,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun dreamingToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to DREAMING
@@ -1598,6 +1621,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun aodToPrimaryBouncer() =
         testScope.runTest {
             // GIVEN a prior transition has run to AOD
@@ -1699,6 +1723,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun lockscreenToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to LOCKSCREEN
@@ -1756,6 +1781,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun glanceableHubToLockscreen() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1810,6 +1836,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun glanceableHubToDozing() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1831,6 +1858,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun glanceableHubToPrimaryBouncer() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
@@ -1852,6 +1880,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun glanceableHubToAlternateBouncer() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
@@ -1904,6 +1933,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun glanceableHubToGone() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1925,6 +1955,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun glanceableHubToDreaming() =
         testScope.runTest {
             // GIVEN that we are dreaming and not dozing
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index b1a8dd1..a77169e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -18,20 +18,29 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertEquals
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -57,14 +66,22 @@
                 .thenReturn(surfaceBehindIsAnimatingFlow)
         }
 
-    private val underTest = kosmos.windowManagerLockscreenVisibilityInteractor
+    private val underTest = lazy { kosmos.windowManagerLockscreenVisibilityInteractor }
     private val testScope = kosmos.testScope
     private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
 
+    @Before
+    fun setUp() {
+        // lazy value needs to be called here otherwise flow collection misbehaves
+        underTest.value
+        kosmos.sceneContainerRepository.setTransitionState(sceneTransitions)
+    }
+
     @Test
+    @DisableSceneContainer
     fun surfaceBehindVisibility_switchesToCorrectFlow() =
         testScope.runTest {
-            val values by collectValues(underTest.surfaceBehindVisibility)
+            val values by collectValues(underTest.value.surfaceBehindVisibility)
 
             // Start on LOCKSCREEN.
             transitionRepository.sendTransitionStep(
@@ -170,9 +187,10 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun testUsingGoingAwayAnimation_duringTransitionToGone() =
         testScope.runTest {
-            val values by collectValues(underTest.usingKeyguardGoingAwayAnimation)
+            val values by collectValues(underTest.value.usingKeyguardGoingAwayAnimation)
 
             // Start on LOCKSCREEN.
             transitionRepository.sendTransitionStep(
@@ -230,9 +248,10 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun testNotUsingGoingAwayAnimation_evenWhenAnimating_ifStateIsNotGone() =
         testScope.runTest {
-            val values by collectValues(underTest.usingKeyguardGoingAwayAnimation)
+            val values by collectValues(underTest.value.usingKeyguardGoingAwayAnimation)
 
             // Start on LOCKSCREEN.
             transitionRepository.sendTransitionStep(
@@ -319,9 +338,10 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun lockscreenVisibility_visibleWhenGone() =
         testScope.runTest {
-            val values by collectValues(underTest.lockscreenVisibility)
+            val values by collectValues(underTest.value.lockscreenVisibility)
 
             // Start on LOCKSCREEN.
             transitionRepository.sendTransitionStep(
@@ -385,9 +405,10 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun testLockscreenVisibility_usesFromState_ifCanceled() =
         testScope.runTest {
-            val values by collectValues(underTest.lockscreenVisibility)
+            val values by collectValues(underTest.value.lockscreenVisibility)
 
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
@@ -486,9 +507,10 @@
      * state during the AOD/isAsleep -> GONE transition is AOD (where lockscreen visibility = true).
      */
     @Test
+    @DisableSceneContainer
     fun testLockscreenVisibility_falseDuringTransitionToGone_fromCanceledGone() =
         testScope.runTest {
-            val values by collectValues(underTest.lockscreenVisibility)
+            val values by collectValues(underTest.value.lockscreenVisibility)
 
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
@@ -587,11 +609,11 @@
             )
         }
 
-    /**  */
     @Test
+    @DisableSceneContainer
     fun testLockscreenVisibility_trueDuringTransitionToGone_fromNotCanceledGone() =
         testScope.runTest {
-            val values by collectValues(underTest.lockscreenVisibility)
+            val values by collectValues(underTest.value.lockscreenVisibility)
 
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
@@ -702,4 +724,109 @@
                 values
             )
         }
+
+    @Test
+    @EnableSceneContainer
+    fun sceneContainer_lockscreenVisibility_visibleWhenNotGone() =
+        testScope.runTest {
+            val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility)
+
+            sceneTransitions.value = lsToGone
+            assertThat(lockscreenVisibility).isTrue()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = goneToLs
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+            assertThat(lockscreenVisibility).isTrue()
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun sceneContainer_lockscreenVisibility_notVisibleWhenReturningToGone() =
+        testScope.runTest {
+            val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility)
+
+            sceneTransitions.value = goneToLs
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = lsToGone
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = goneToLs
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+            assertThat(lockscreenVisibility).isTrue()
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun sceneContainer_usingGoingAwayAnimation_duringTransitionToGone() =
+        testScope.runTest {
+            val usingKeyguardGoingAwayAnimation by
+                collectLastValue(underTest.value.usingKeyguardGoingAwayAnimation)
+
+            sceneTransitions.value = lsToGone
+            assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+            assertThat(usingKeyguardGoingAwayAnimation).isFalse()
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun sceneContainer_usingGoingAwayAnimation_surfaceBehindIsAnimating() =
+        testScope.runTest {
+            val usingKeyguardGoingAwayAnimation by
+                collectLastValue(underTest.value.usingKeyguardGoingAwayAnimation)
+
+            sceneTransitions.value = lsToGone
+            surfaceBehindIsAnimatingFlow.emit(true)
+            assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+            assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+            sceneTransitions.value = goneToLs
+            assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+            surfaceBehindIsAnimatingFlow.emit(false)
+            assertThat(usingKeyguardGoingAwayAnimation).isFalse()
+        }
+
+    companion object {
+        private val progress = MutableStateFlow(0f)
+
+        private val sceneTransitions =
+            MutableStateFlow<ObservableTransitionState>(
+                ObservableTransitionState.Idle(Scenes.Lockscreen)
+            )
+
+        private val lsToGone =
+            ObservableTransitionState.Transition(
+                Scenes.Lockscreen,
+                Scenes.Gone,
+                flowOf(Scenes.Lockscreen),
+                progress,
+                false,
+                flowOf(false)
+            )
+
+        private val goneToLs =
+            ObservableTransitionState.Transition(
+                Scenes.Gone,
+                Scenes.Lockscreen,
+                flowOf(Scenes.Lockscreen),
+                progress,
+                false,
+                flowOf(false)
+            )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
new file mode 100644
index 0000000..8a5af09
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
@@ -0,0 +1,1318 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor.scenetransition
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.realKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenSceneTransitionInteractorTest : SysuiTestCase() {
+    private val kosmos =
+        testKosmos().apply { keyguardTransitionRepository = realKeyguardTransitionRepository }
+
+    private val testScope = kosmos.testScope
+    private val underTest = kosmos.lockscreenSceneTransitionInteractor
+
+    private val progress = MutableStateFlow(0f)
+
+    private val sceneTransitions =
+        MutableStateFlow<ObservableTransitionState>(
+            ObservableTransitionState.Idle(Scenes.Lockscreen)
+        )
+
+    private val lsToGone =
+        ObservableTransitionState.Transition(
+            Scenes.Lockscreen,
+            Scenes.Gone,
+            flowOf(Scenes.Lockscreen),
+            progress,
+            false,
+            flowOf(false)
+        )
+
+    private val goneToLs =
+        ObservableTransitionState.Transition(
+            Scenes.Gone,
+            Scenes.Lockscreen,
+            flowOf(Scenes.Lockscreen),
+            progress,
+            false,
+            flowOf(false)
+        )
+
+    @Before
+    fun setUp() {
+        underTest.start()
+        kosmos.sceneContainerRepository.setTransitionState(sceneTransitions)
+        testScope.launch {
+            kosmos.realKeyguardTransitionRepository.emitInitialStepsFromOff(
+                KeyguardState.LOCKSCREEN
+            )
+        }
+    }
+
+    /** STL: Ls -> Gone, then settle with Idle(Gone). This is the default case. */
+    @Test
+    fun transition_from_ls_scene_end_in_gone() =
+        testScope.runTest {
+            sceneTransitions.value = lsToGone
+
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0f,
+            )
+
+            progress.value = 0.4f
+            assertTransition(
+                step = currentStep!!,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+        }
+
+    /**
+     * STL: Ls -> Gone, then settle with Idle(Ls). KTF in this scenario needs to invert the
+     * transition LS -> UNDEFINED to UNDEFINED -> LS as there is no mechanism in KTF to
+     * finish/settle to progress 0.0f.
+     */
+    @Test
+    fun transition_from_ls_scene_end_in_ls() =
+        testScope.runTest {
+            sceneTransitions.value = lsToGone
+
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0f,
+            )
+
+            progress.value = 0.4f
+            assertTransition(
+                step = currentStep!!,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+
+            assertTransition(
+                step = allSteps[allSteps.size - 3],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.CANCELED,
+                progress = 0.4f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 2],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.STARTED,
+                progress = 0.6f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 1],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+        }
+
+    /**
+     * STL: Ls -> Gone, then settle with Idle(Ls). KTF starts in AOD and needs to inverse correctly
+     * back to AOD.
+     */
+    @Test
+    fun transition_from_ls_scene_on_aod_end_in_ls() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+
+            kosmos.realKeyguardTransitionRepository.startTransition(
+                TransitionInfo(
+                    ownerName = this.javaClass.simpleName,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    animator = null,
+                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                )
+            )
+            sceneTransitions.value = lsToGone
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.AOD,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0f,
+            )
+
+            progress.value = 0.4f
+            assertTransition(
+                step = currentStep!!,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+
+            assertTransition(
+                step = allSteps[allSteps.size - 3],
+                from = KeyguardState.AOD,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.CANCELED,
+                progress = 0.4f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 2],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.AOD,
+                state = TransitionState.STARTED,
+                progress = 0.6f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 1],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.AOD,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then settle with Idle(Ls). This is the default case in the reverse
+     * direction.
+     */
+    @Test
+    fun transition_to_ls_scene_end_in_ls() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            sceneTransitions.value = goneToLs
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0f,
+            )
+
+            progress.value = 0.4f
+            assertTransition(
+                step = currentStep!!,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+        }
+
+    /** STL: Gone -> Ls (AOD), will transition to AOD once */
+    @Test
+    fun transition_to_ls_scene_with_changed_next_scene_is_respected_just_once() =
+        testScope.runTest {
+            underTest.onSceneAboutToChange(Scenes.Lockscreen, KeyguardState.AOD)
+            sceneTransitions.value = goneToLs
+
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.AOD,
+                state = TransitionState.RUNNING,
+                progress = 0f,
+            )
+
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Shade,
+                    Scenes.Lockscreen,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then settle with Idle(Gone). KTF in this scenario needs to invert the
+     * transition UNDEFINED -> LS to LS -> UNDEFINED as there is no mechanism in KTF to
+     * finish/settle to progress 0.0f.
+     */
+    @Test
+    fun transition_to_ls_scene_end_in_from_scene() =
+        testScope.runTest {
+            sceneTransitions.value = goneToLs
+
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0f,
+            )
+
+            progress.value = 0.4f
+            assertTransition(
+                step = currentStep!!,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+            val stepM3 = allSteps[allSteps.size - 3]
+            val stepM2 = allSteps[allSteps.size - 2]
+
+            assertTransition(
+                step = stepM3,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.CANCELED,
+                progress = 0.4f,
+            )
+
+            assertTransition(
+                step = stepM2,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.STARTED,
+                progress = 0.6f,
+            )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then interrupted by Shade -> Ls. KTF in this scenario needs to invert the
+     * transition UNDEFINED -> LS to LS -> UNDEFINED as there is no mechanism in KTF to
+     * finish/settle to progress 0.0f. Then restart a different transition UNDEFINED -> Ls.
+     */
+    @Test
+    fun transition_to_ls_scene_end_in_to_ls_transition() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+            sceneTransitions.value = goneToLs
+            progress.value = 0.4f
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Shade,
+                    Scenes.Lockscreen,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 5],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.CANCELED,
+                progress = 0.4f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 4],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.STARTED,
+                progress = 0.6f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 3],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 2],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            progress.value = 0.2f
+            assertTransition(
+                step = allSteps[allSteps.size - 1],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0.2f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then interrupted by Ls -> Shade. This is like continuing the transition from
+     * Ls before the transition before has properly settled. This can happen in STL e.g. with an
+     * accelerated swipe (quick successive fling gestures).
+     */
+    @Test
+    fun transition_to_ls_scene_end_in_from_ls_transition() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+            sceneTransitions.value = goneToLs
+            progress.value = 0.4f
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Lockscreen,
+                    Scenes.Shade,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 3],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.CANCELED,
+                progress = 0.4f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 2],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.STARTED,
+                progress = 0.0f,
+            )
+
+            progress.value = 0.2f
+            assertTransition(
+                step = allSteps[allSteps.size - 1],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0.2f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then interrupted by Gone -> Shade. This is going back to Gone but starting a
+     * transition from Gone before settling in Gone. KTF needs to make sure the transition is
+     * properly inversed and settled in UNDEFINED.
+     */
+    @Test
+    fun transition_to_ls_scene_end_in_other_transition() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+            sceneTransitions.value = goneToLs
+            progress.value = 0.4f
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Gone,
+                    Scenes.Shade,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 3],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.CANCELED,
+                progress = 0.4f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 2],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.STARTED,
+                progress = 0.6f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 1],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then interrupt in KTF LS -> AOD, then stl still finishes in Ls. After a KTF
+     * transition is started (UNDEFINED -> LOCKSCREEN) KTF immediately considers the active scene to
+     * be LOCKSCREEN. This means that all listeners for LOCKSCREEN are active and may start a new
+     * transition LOCKSCREEN -> *. Here we test LS -> AOD.
+     *
+     * KTF is allowed to already start and play the other transition, while the STL transition may
+     * finish later (gesture completes much later). When we eventually settle the STL transition in
+     * Ls we do not want to force KTF back to its original destination (LOCKSCREEN). Instead, for
+     * this scenario the settle can be ignored.
+     */
+    @Test
+    fun transition_to_ls_scene_interrupted_by_ktf_transition_then_finish_in_lockscreen() =
+        testScope.runTest {
+            sceneTransitions.value = goneToLs
+
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0f,
+            )
+
+            progress.value = 0.4f
+            assertTransition(
+                step = currentStep!!,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            kosmos.realKeyguardTransitionRepository.startTransition(
+                TransitionInfo(
+                    ownerName = this.javaClass.simpleName,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    animator = null,
+                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                )
+            )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            // Scene progress should not affect KTF transition anymore
+            progress.value = 0.7f
+            assertTransition(currentStep!!, progress = 0f)
+
+            // Scene transition still finishes but should not impact KTF transition
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then interrupt in KTF LS -> AOD, then stl finishes in Gone.
+     *
+     * Refers to: `transition_to_ls_scene_interrupted_by_ktf_transition_then_finish_in_lockscreen`
+     *
+     * This is similar to the previous scenario but the gesture may have gone back to its origin. In
+     * this case we can not ignore the settlement, because whatever KTF has done in the meantime it
+     * needs to immediately finish in UNDEFINED (there is a jump cut).
+     */
+    @Test
+    fun transition_to_ls_scene_interrupted_by_ktf_transition_then_finish_in_gone() =
+        testScope.runTest {
+            sceneTransitions.value = goneToLs
+
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0f,
+            )
+
+            progress.value = 0.4f
+            assertTransition(
+                step = currentStep!!,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            kosmos.realKeyguardTransitionRepository.startTransition(
+                TransitionInfo(
+                    ownerName = this.javaClass.simpleName,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    animator = null,
+                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                )
+            )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            progress.value = 0.7f
+            assertThat(currentStep?.value).isEqualTo(0f)
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.AOD,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then interrupt in KTF LS -> AOD, then STL Gone -> Shade
+     *
+     * Refers to: `transition_to_ls_scene_interrupted_by_ktf_transition_then_finish_in_lockscreen`
+     *
+     * This is similar to the previous scenario but the gesture may have been interrupted by any
+     * other transition. KTF needs to immediately finish in UNDEFINED (there is a jump cut).
+     */
+    @Test
+    fun transition_to_ls_interrupted_by_ktf_transition_then_interrupted_by_other_transition() =
+        testScope.runTest {
+            sceneTransitions.value = goneToLs
+
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0f,
+            )
+
+            progress.value = 0.4f
+            assertTransition(
+                step = currentStep!!,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            kosmos.realKeyguardTransitionRepository.startTransition(
+                TransitionInfo(
+                    ownerName = this.javaClass.simpleName,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    animator = null,
+                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                )
+            )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            progress.value = 0.7f
+            assertTransition(currentStep!!, progress = 0f)
+
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Gone,
+                    Scenes.Shade,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.AOD,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then interrupt in KTF LS -> AOD, then STL Ls -> Shade
+     *
+     * In this scenario it is important that the last STL transition Ls -> Shade triggers a cancel
+     * of the * -> AOD transition but then also properly starts a transition AOD (not LOCKSCREEN) ->
+     * UNDEFINED transition.
+     */
+    @Test
+    fun transition_to_ls_interrupted_by_ktf_transition_then_interrupted_by_from_ls_transition() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+            sceneTransitions.value = goneToLs
+            progress.value = 0.4f
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            kosmos.realKeyguardTransitionRepository.startTransition(
+                TransitionInfo(
+                    ownerName = this.javaClass.simpleName,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    animator = null,
+                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                )
+            )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            progress.value = 0.7f
+            assertTransition(currentStep!!, progress = 0f)
+
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Lockscreen,
+                    Scenes.Shade,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+            allSteps[allSteps.size - 3]
+
+            assertTransition(
+                step = allSteps[allSteps.size - 3],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.CANCELED,
+                progress = 0f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 2],
+                from = KeyguardState.AOD,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            progress.value = 0.2f
+            assertTransition(
+                step = allSteps[allSteps.size - 1],
+                from = KeyguardState.AOD,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0.2f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then interrupt in KTF LS -> AOD, then STL Shade -> Ls
+     *
+     * In this scenario it is important KTF is brought back into a FINISHED UNDEFINED state
+     * considering the state is already on AOD from where a new UNDEFINED -> LOCKSCREEN transition
+     * can be started.
+     */
+    @Test
+    fun transition_to_ls_interrupted_by_ktf_transition_then_interrupted_by_to_ls_transition() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+            sceneTransitions.value = goneToLs
+            progress.value = 0.4f
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            kosmos.realKeyguardTransitionRepository.startTransition(
+                TransitionInfo(
+                    ownerName = this.javaClass.simpleName,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    animator = null,
+                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                )
+            )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            progress.value = 0.7f
+            assertTransition(currentStep!!, progress = 0f)
+
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Shade,
+                    Scenes.Lockscreen,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 5],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.CANCELED,
+                progress = 0f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 4],
+                from = KeyguardState.AOD,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.STARTED,
+                progress = 1f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 3],
+                from = KeyguardState.AOD,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 2],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 1],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0.7f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then interrupt multiple canceled KTF transitions, then STL Ls -> Shade
+     *
+     * Similar to
+     * `transition_to_ls_scene_interrupted_by_ktf_transition_then_interrupted_by_from_ls_transition`
+     * but here KTF is canceled multiple times such that in the end OCCLUDED -> UNDEFINED is
+     * properly started. (not from AOD or LOCKSCREEN)
+     *
+     * Note: there is no test which tests multiple cancels from the STL side, this is because all
+     * STL transitions trigger a response from LockscreenSceneTransitionInteractor which forces KTF
+     * into a specific state, so testing each pair is enough. Meanwhile KTF can move around without
+     * any reaction from LockscreenSceneTransitionInteractor.
+     */
+    @Test
+    fun transition_to_ls_interrupted_by_ktf_cancel_sequence_interrupted_by_from_ls_transition() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+            sceneTransitions.value = lsToGone
+            progress.value = 0.4f
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            kosmos.realKeyguardTransitionRepository.startTransition(
+                TransitionInfo(
+                    ownerName = this.javaClass.simpleName,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    animator = null,
+                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                )
+            )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            kosmos.realKeyguardTransitionRepository.startTransition(
+                TransitionInfo(
+                    ownerName = this.javaClass.simpleName,
+                    from = KeyguardState.AOD,
+                    to = KeyguardState.DOZING,
+                    animator = null,
+                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                )
+            )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.AOD,
+                to = KeyguardState.DOZING,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            kosmos.realKeyguardTransitionRepository.startTransition(
+                TransitionInfo(
+                    ownerName = this.javaClass.simpleName,
+                    from = KeyguardState.DOZING,
+                    to = KeyguardState.OCCLUDED,
+                    animator = null,
+                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                )
+            )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.DOZING,
+                to = KeyguardState.OCCLUDED,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            progress.value = 0.7f
+            assertTransition(currentStep!!, progress = 0f)
+
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Lockscreen,
+                    Scenes.Shade,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 3],
+                from = KeyguardState.DOZING,
+                to = KeyguardState.OCCLUDED,
+                state = TransitionState.CANCELED,
+                progress = 0f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 2],
+                from = KeyguardState.OCCLUDED,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            progress.value = 0.2f
+            assertTransition(
+                step = allSteps[allSteps.size - 1],
+                from = KeyguardState.OCCLUDED,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0.2f,
+            )
+        }
+
+    /**
+     * STL: Gone -> Ls, then interrupted by KTF LS -> AOD which is FINISHED before STL Ls -> Shade
+     *
+     * Similar to
+     * `transition_to_ls_scene_interrupted_by_ktf_transition_then_interrupted_by_from_ls_transition`
+     * but here KTF is finishing the transition and only then gets interrupted. Should correctly
+     * start AOD -> UNDEFINED.
+     */
+    @Test
+    fun transition_to_ls_scene_interrupted_and_finished_by_ktf_interrupted_by_from_ls_transition() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+            sceneTransitions.value = lsToGone
+            progress.value = 0.4f
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            val ktfUuid =
+                kosmos.realKeyguardTransitionRepository.startTransition(
+                    TransitionInfo(
+                        ownerName = this.javaClass.simpleName,
+                        from = KeyguardState.LOCKSCREEN,
+                        to = KeyguardState.AOD,
+                        animator = null,
+                        modeOnCanceled = TransitionModeOnCanceled.RESET
+                    )
+                )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            kosmos.realKeyguardTransitionRepository.updateTransition(
+                ktfUuid!!,
+                1f,
+                TransitionState.FINISHED
+            )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Lockscreen,
+                    Scenes.Shade,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 3],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 2],
+                from = KeyguardState.AOD,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            progress.value = 0.2f
+            assertTransition(
+                step = allSteps[allSteps.size - 1],
+                from = KeyguardState.AOD,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0.2f,
+            )
+        }
+
+    /**
+     * STL: Ls -> Gone, then interrupted by Ls -> Bouncer. This happens when the next transition is
+     * immediately started from Gone without settling in Idle. This specifically happens when
+     * dragging down on Ls and then changing direction. The transition will switch from -> Shade to
+     * -> Bouncer without settling or signaling any cancellation as STL considers this to be the
+     * same gesture.
+     *
+     * In STL there is no guarantee that transitions settle in Idle before continuing.
+     */
+    @Test
+    fun transition_from_ls_scene_interrupted_by_other_from_ls_transition() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+            sceneTransitions.value = lsToGone
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0f,
+            )
+
+            progress.value = 0.4f
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Lockscreen,
+                    Scenes.Bouncer,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 5],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.CANCELED,
+                progress = 0.4f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 4],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.STARTED,
+                progress = 0.6f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 3],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 2],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+        }
+
+    /**
+     * STL: Ls -> Gone, then interrupted by Gone -> Ls. This happens when the next transition is
+     * immediately started from Gone without settling in Idle. In STL there is no guarantee that
+     * transitions settle in Idle before continuing.
+     */
+    @Test
+    fun transition_from_ls_scene_interrupted_by_to_ls_transition() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+            sceneTransitions.value = lsToGone
+            progress.value = 0.4f
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Gone,
+                    Scenes.Lockscreen,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 3],
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+
+            assertTransition(
+                step = allSteps[allSteps.size - 2],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.STARTED,
+                progress = 0f,
+            )
+
+            progress.value = 0.2f
+            assertTransition(
+                step = allSteps[allSteps.size - 1],
+                from = KeyguardState.UNDEFINED,
+                to = KeyguardState.LOCKSCREEN,
+                state = TransitionState.RUNNING,
+                progress = 0.2f,
+            )
+        }
+
+    /**
+     * STL: Ls -> Gone, then interrupted by Gone -> Bouncer. This happens when the next transition
+     * is immediately started from Gone without settling in Idle. In STL there is no guarantee that
+     * transitions settle in Idle before continuing.
+     */
+    @Test
+    fun transition_from_ls_scene_interrupted_by_other_stl_transition() =
+        testScope.runTest {
+            val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+            sceneTransitions.value = lsToGone
+            progress.value = 0.4f
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.RUNNING,
+                progress = 0.4f,
+            )
+
+            sceneTransitions.value =
+                ObservableTransitionState.Transition(
+                    Scenes.Gone,
+                    Scenes.Bouncer,
+                    flowOf(Scenes.Lockscreen),
+                    progress,
+                    false,
+                    flowOf(false)
+                )
+
+            assertTransition(
+                step = currentStep!!,
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.UNDEFINED,
+                state = TransitionState.FINISHED,
+                progress = 1f,
+            )
+        }
+
+    private fun assertTransition(
+        step: TransitionStep,
+        from: KeyguardState? = null,
+        to: KeyguardState? = null,
+        state: TransitionState? = null,
+        progress: Float? = null
+    ) {
+        if (from != null) assertThat(step.from).isEqualTo(from)
+        if (to != null) assertThat(step.to).isEqualTo(to)
+        if (state != null) assertThat(step.transitionState).isEqualTo(state)
+        if (progress != null) assertThat(step.value).isEqualTo(progress)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt
index 2f650c4..040d3b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt
@@ -85,7 +85,6 @@
         setupWeatherClock()
         KeyguardClockViewBinder.updateBurnInLayer(rootView, clockViewModel, ClockSize.LARGE)
         verify(burnInLayer).removeView(smallClockView)
-        verify(burnInLayer).addView(largeClockView)
     }
 
     @Test
@@ -101,7 +100,6 @@
         setupNonWeatherClock()
         KeyguardClockViewBinder.updateBurnInLayer(rootView, clockViewModel, ClockSize.SMALL)
         verify(burnInLayer).addView(smallClockView)
-        verify(burnInLayer).removeView(largeClockView)
     }
 
     private fun setupWeatherClock() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 9b2db3e..1f13298 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -21,6 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
@@ -41,10 +42,9 @@
     private lateinit var executor: FakeExecutor
 
     @Mock private lateinit var activityTaskManagerService: IActivityTaskManager
-
     @Mock private lateinit var keyguardStateController: KeyguardStateController
-
     @Mock private lateinit var keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier
+    @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
 
     @Before
     fun setUp() {
@@ -57,6 +57,7 @@
                 activityTaskManagerService = activityTaskManagerService,
                 keyguardStateController = keyguardStateController,
                 keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator,
+                keyguardTransitionInteractor = keyguardTransitionInteractor,
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index ba2efe6..b3cc5c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardSmartspaceInteractor
 import com.android.systemui.keyguard.shared.model.ClockSize
 import com.android.systemui.keyguard.ui.viewmodel.keyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardSmartspaceViewModel
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
@@ -117,6 +118,7 @@
                     context,
                     keyguardSmartspaceViewModel,
                     { keyguardBlueprintInteractor },
+                    keyguardRootViewModel,
                 )
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
index 1396b20..391831a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -18,15 +18,19 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import androidx.test.filters.SmallTest
+import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.policy.keyguardStateController
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -34,6 +38,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import org.mockito.kotlin.whenever
 
 @ExperimentalCoroutinesApi
 @RunWith(JUnit4::class)
@@ -49,13 +54,35 @@
     fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() =
         testScope.runTest {
             mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+            val canShowAlternateBouncer by collectLastValue(underTest.canShowAlternateBouncer)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
+            givenCanShowAlternateBouncer()
             fingerprintPropertyRepository.supportsUdfps()
             transitionRepository.sendTransitionSteps(
                 listOf(
+                    stepToLockscreen(0f, TransitionState.STARTED),
+                    stepToLockscreen(.4f),
+                    stepToLockscreen(1f, TransitionState.FINISHED),
+                ),
+                testScope,
+            )
+            assertThat(canShowAlternateBouncer).isTrue()
+            transitionRepository.sendTransitionSteps(
+                listOf(
+                    stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
+                    stepFromLockscreenToAlternateBouncer(.4f),
+                    stepFromLockscreenToAlternateBouncer(.6f),
+                ),
+                testScope,
+            )
+            assertThat(canShowAlternateBouncer).isTrue()
+            assertThat(alternateBouncerWindowRequired).isTrue()
+
+            transitionRepository.sendTransitionSteps(
+                listOf(
                     stepFromAlternateBouncer(0f, TransitionState.STARTED),
-                    stepFromAlternateBouncer(.4f),
+                    stepFromAlternateBouncer(.2f),
                     stepFromAlternateBouncer(.6f),
                 ),
                 testScope,
@@ -77,13 +104,21 @@
             mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
+            givenCanShowAlternateBouncer()
             fingerprintPropertyRepository.supportsUdfps()
             transitionRepository.sendTransitionSteps(
                 listOf(
-                    stepFromAlternateBouncer(0f, TransitionState.STARTED),
-                    stepFromAlternateBouncer(.4f),
-                    stepFromAlternateBouncer(.6f),
-                    stepFromAlternateBouncer(1f),
+                    stepToLockscreen(0f, TransitionState.STARTED),
+                    stepToLockscreen(.4f),
+                    stepToLockscreen(1f, TransitionState.FINISHED),
+                ),
+                testScope,
+            )
+            transitionRepository.sendTransitionSteps(
+                listOf(
+                    stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
+                    stepFromLockscreenToAlternateBouncer(.4f),
+                    stepFromLockscreenToAlternateBouncer(.6f),
                 ),
                 testScope,
             )
@@ -96,13 +131,23 @@
             mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
+            givenCanShowAlternateBouncer()
             fingerprintPropertyRepository.supportsUdfps()
             transitionRepository.sendTransitionSteps(
                 listOf(
+                    stepFromLockscreenToDozing(0f, TransitionState.STARTED),
+                    stepFromLockscreenToDozing(.4f),
+                    stepFromLockscreenToDozing(.6f),
+                    stepFromLockscreenToDozing(1f, TransitionState.FINISHED),
+                ),
+                testScope,
+            )
+            assertThat(alternateBouncerWindowRequired).isFalse()
+            transitionRepository.sendTransitionSteps(
+                listOf(
                     stepFromDozingToLockscreen(0f, TransitionState.STARTED),
                     stepFromDozingToLockscreen(.4f),
                     stepFromDozingToLockscreen(.6f),
-                    stepFromDozingToLockscreen(1f),
                 ),
                 testScope,
             )
@@ -115,19 +160,39 @@
             mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
+            givenCanShowAlternateBouncer()
             fingerprintPropertyRepository.supportsRearFps()
             transitionRepository.sendTransitionSteps(
                 listOf(
-                    stepFromAlternateBouncer(0f, TransitionState.STARTED),
-                    stepFromAlternateBouncer(.4f),
-                    stepFromAlternateBouncer(.6f),
-                    stepFromAlternateBouncer(1f),
+                    stepToLockscreen(0f, TransitionState.STARTED),
+                    stepToLockscreen(.4f),
+                    stepToLockscreen(1f, TransitionState.FINISHED),
+                ),
+                testScope,
+            )
+            transitionRepository.sendTransitionSteps(
+                listOf(
+                    stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
+                    stepFromLockscreenToAlternateBouncer(.4f),
+                    stepFromLockscreenToAlternateBouncer(.6f),
                 ),
                 testScope,
             )
             assertThat(alternateBouncerWindowRequired).isFalse()
         }
 
+    private fun stepToLockscreen(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return step(
+            from = KeyguardState.GONE,
+            to = KeyguardState.LOCKSCREEN,
+            value = value,
+            transitionState = state,
+        )
+    }
+
     private fun stepFromAlternateBouncer(
         value: Float,
         state: TransitionState = TransitionState.RUNNING
@@ -140,6 +205,18 @@
         )
     }
 
+    private fun stepFromLockscreenToAlternateBouncer(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return step(
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.ALTERNATE_BOUNCER,
+            value = value,
+            transitionState = state,
+        )
+    }
+
     private fun stepFromDozingToLockscreen(
         value: Float,
         state: TransitionState = TransitionState.RUNNING
@@ -152,6 +229,18 @@
         )
     }
 
+    private fun stepFromLockscreenToDozing(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return step(
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.DOZING,
+            value = value,
+            transitionState = state,
+        )
+    }
+
     private fun step(
         from: KeyguardState,
         to: KeyguardState,
@@ -166,4 +255,16 @@
             ownerName = "AlternateBouncerViewModelTest"
         )
     }
+
+    /**
+     * Given the alternate bouncer parameters are set so that the alternate bouncer can show, aside
+     * from the fingerprint modality.
+     */
+    private fun givenCanShowAlternateBouncer() {
+        kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(false)
+        kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+        kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+        whenever(kosmos.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
+        whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
index 768d446..40663ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
 import com.android.systemui.keyguard.data.repository.keyguardClockRepository
@@ -56,7 +57,7 @@
 class KeyguardClockViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     val kosmos = testKosmos()
     val testScope = kosmos.testScope
-    val underTest = kosmos.keyguardClockViewModel
+    val underTest by lazy { kosmos.keyguardClockViewModel }
     val res = context.resources
 
     @Mock lateinit var clockController: ClockController
@@ -96,6 +97,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun currentClockLayout_splitShadeOn_clockNotCentered_largeClock_splitShadeLargeClock() =
         testScope.runTest {
             val currentClockLayout by collectLastValue(underTest.currentClockLayout)
@@ -110,6 +112,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun currentClockLayout_splitShadeOn_clockNotCentered_smallClock_splitShadeSmallClock() =
         testScope.runTest {
             val currentClockLayout by collectLastValue(underTest.currentClockLayout)
@@ -124,6 +127,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun currentClockLayout_singleShade_smallClock_smallClock() =
         testScope.runTest {
             val currentClockLayout by collectLastValue(underTest.currentClockLayout)
@@ -193,6 +197,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun testClockSize_dynamicClockSize() =
         testScope.runTest {
             with(kosmos) {
@@ -216,6 +221,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun isLargeClockVisible_whenSmallClockSize_isFalse() =
         testScope.runTest {
             val value by collectLastValue(underTest.isLargeClockVisible)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
index e56a253..5986f4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
@@ -33,9 +33,6 @@
 import com.android.systemui.media.controls.util.MediaUiEventLogger
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.Executor
@@ -50,6 +47,9 @@
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
 
 private const val KEY = "TEST_KEY"
 private const val KEY_ALT = "TEST_KEY_2"
@@ -172,20 +172,20 @@
     fun testOnRemovedForCurrent_callsListener() {
         // GIVEN a media was removed for main user
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
-        mediaDataFilter.onMediaDataRemoved(KEY)
+        mediaDataFilter.onMediaDataRemoved(KEY, false)
 
         // THEN we should tell the listener
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
     fun testOnRemovedForGuest_doesNotCallListener() {
         // GIVEN a media was removed for guest user
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
-        mediaDataFilter.onMediaDataRemoved(KEY)
+        mediaDataFilter.onMediaDataRemoved(KEY, false)
 
         // THEN we should NOT tell the listener
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
     }
 
     @Test
@@ -197,7 +197,7 @@
         setUser(USER_GUEST)
 
         // THEN we should remove the main user's media
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -230,7 +230,7 @@
         setPrivateProfileUnavailable()
 
         // THEN we should add the private profile media
-        verify(listener).onMediaDataRemoved(eq(KEY_ALT))
+        verify(listener).onMediaDataRemoved(eq(KEY_ALT), eq(false))
     }
 
     @Test
@@ -360,7 +360,7 @@
     @Test
     fun testOnNotificationRemoved_doesntHaveMedia() {
         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
-        mediaDataFilter.onMediaDataRemoved(KEY)
+        mediaDataFilter.onMediaDataRemoved(KEY, false)
         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
index 5a2d22d..3372f06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
@@ -66,9 +66,6 @@
 import com.android.systemui.statusbar.SbnBuilder
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
@@ -90,6 +87,9 @@
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoSession
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
 import org.mockito.quality.Strictness
 
 private const val KEY = "KEY"
@@ -346,7 +346,7 @@
         // THEN it is removed and listeners are informed
         foregroundExecutor.advanceClockToLast()
         foregroundExecutor.runAllReady()
-        verify(listener).onMediaDataRemoved(PACKAGE_NAME)
+        verify(listener).onMediaDataRemoved(PACKAGE_NAME, false)
     }
 
     @Test
@@ -532,7 +532,7 @@
         addNotificationAndLoad()
         val data = mediaDataCaptor.value
         mediaDataManager.onNotificationRemoved(KEY)
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
@@ -777,7 +777,7 @@
                 eq(false)
             )
         assertThat(mediaDataCaptor.value.resumption).isTrue()
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), eq(false))
         // WHEN the second is removed
         mediaDataManager.onNotificationRemoved(KEY_2)
         // THEN the data is for resumption and the second key is removed
@@ -791,7 +791,7 @@
                 eq(false)
             )
         assertThat(mediaDataCaptor.value.resumption).isTrue()
-        verify(listener).onMediaDataRemoved(eq(KEY_2))
+        verify(listener).onMediaDataRemoved(eq(KEY_2), eq(false))
     }
 
     @Test
@@ -816,7 +816,7 @@
         mediaDataManager.onNotificationRemoved(KEY)
 
         // THEN the media data is removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -866,7 +866,7 @@
         mediaDataManager.onNotificationRemoved(KEY)
 
         // THEN the media data is removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -905,7 +905,7 @@
         assertThat(mediaDataCaptor.value.isPlaying).isFalse()
 
         // And the oldest resume control was removed
-        verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"))
+        verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"), eq(false))
     }
 
     fun testOnNotificationRemoved_lockDownMode() {
@@ -915,7 +915,7 @@
         val data = mediaDataCaptor.value
         mediaDataManager.onNotificationRemoved(KEY)
 
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         verify(logger, never())
             .logActiveConvertedToResume(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
@@ -1148,7 +1148,7 @@
         mediaDataManager.setMediaResumptionEnabled(false)
 
         // THEN the resume controls are dismissed
-        verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME))
+        verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
@@ -1156,19 +1156,19 @@
     fun testDismissMedia_listenerCalled() {
         addNotificationAndLoad()
         val data = mediaDataCaptor.value
-        val removed = mediaDataManager.dismissMediaData(KEY, 0L)
+        val removed = mediaDataManager.dismissMediaData(KEY, 0L, true)
         assertThat(removed).isTrue()
 
         foregroundExecutor.advanceClockToLast()
         foregroundExecutor.runAllReady()
 
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
     @Test
     fun testDismissMedia_keyDoesNotExist_returnsFalse() {
-        val removed = mediaDataManager.dismissMediaData(KEY, 0L)
+        val removed = mediaDataManager.dismissMediaData(KEY, 0L, true)
         assertThat(removed).isFalse()
     }
 
@@ -2077,7 +2077,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It remains as a regular player
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
     }
@@ -2093,7 +2093,7 @@
         mediaDataManager.onNotificationRemoved(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2146,7 +2146,7 @@
         mediaDataManager.onNotificationRemoved(KEY)
 
         // It remains as a regular player
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
     }
@@ -2199,7 +2199,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2253,7 +2253,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed.
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(
@@ -2279,7 +2279,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2329,7 +2329,7 @@
         mediaDataManager.onNotificationRemoved(KEY)
 
         // We still make sure to remove it
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
index bb5b572..dd05a0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
@@ -202,24 +202,24 @@
     @Test
     public void mediaDataRemoved() {
         // WHEN media data is removed without first receiving device or data
-        mManager.onMediaDataRemoved(KEY);
+        mManager.onMediaDataRemoved(KEY, false);
         // THEN a removed event isn't emitted
-        verify(mListener, never()).onMediaDataRemoved(eq(KEY));
+        verify(mListener, never()).onMediaDataRemoved(eq(KEY), anyBoolean());
     }
 
     @Test
     public void mediaDataRemovedAfterMediaEvent() {
         mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
                 0 /* receivedSmartspaceCardLatency */, false /* isSsReactivated */);
-        mManager.onMediaDataRemoved(KEY);
-        verify(mListener).onMediaDataRemoved(eq(KEY));
+        mManager.onMediaDataRemoved(KEY, false);
+        verify(mListener).onMediaDataRemoved(eq(KEY), eq(false));
     }
 
     @Test
     public void mediaDataRemovedAfterDeviceEvent() {
         mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
-        mManager.onMediaDataRemoved(KEY);
-        verify(mListener).onMediaDataRemoved(eq(KEY));
+        mManager.onMediaDataRemoved(KEY, false);
+        verify(mListener).onMediaDataRemoved(eq(KEY), eq(false));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index 857af66..caaa42f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -40,9 +40,6 @@
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.Executor
@@ -60,6 +57,9 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
 
 private const val KEY = "TEST_KEY"
 private const val KEY_ALT = "TEST_KEY_2"
@@ -168,7 +168,7 @@
     @Test
     fun onDataLoadedForCurrentUser_updatesLoadedStates() =
         testScope.runTest {
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val mediaCommonModel =
                 MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(dataMain.instanceId))
 
@@ -176,13 +176,13 @@
 
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true), eq(0), eq(false))
-            assertThat(sortedMedia?.values).containsExactly(mediaCommonModel)
+            assertThat(currentMedia).containsExactly(mediaCommonModel)
         }
 
     @Test
     fun onDataLoadedForGuest_doesNotUpdateLoadedStates() =
         testScope.runTest {
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val mediaCommonModel =
                 MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(dataMain.instanceId))
 
@@ -190,64 +190,63 @@
 
             verify(listener, never())
                 .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
-            assertThat(sortedMedia?.values).doesNotContain(mediaCommonModel)
+            assertThat(currentMedia).doesNotContain(mediaCommonModel)
         }
 
     @Test
     fun onRemovedForCurrent_updatesLoadedStates() =
         testScope.runTest {
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val mediaCommonModel =
                 MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(dataMain.instanceId))
 
             // GIVEN a media was removed for main user
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
 
-            assertThat(sortedMedia?.values).containsExactly(mediaCommonModel)
+            assertThat(currentMedia).containsExactly(mediaCommonModel)
 
-            mediaDataFilter.onMediaDataRemoved(KEY)
+            mediaDataFilter.onMediaDataRemoved(KEY, false)
 
-            verify(listener).onMediaDataRemoved(eq(KEY))
-            assertThat(sortedMedia?.values).doesNotContain(mediaCommonModel)
+            verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
+            assertThat(currentMedia).doesNotContain(mediaCommonModel)
         }
 
     @Test
     fun onRemovedForGuest_doesNotUpdateLoadedStates() =
         testScope.runTest {
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
 
             // GIVEN a media was removed for guest user
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
-            mediaDataFilter.onMediaDataRemoved(KEY)
+            mediaDataFilter.onMediaDataRemoved(KEY, false)
 
-            verify(listener, never()).onMediaDataRemoved(eq(KEY))
-            assertThat(sortedMedia).isEmpty()
+            verify(listener, never()).onMediaDataRemoved(eq(KEY), eq(false))
+            assertThat(currentMedia).isEmpty()
         }
 
     @Test
     fun onUserSwitched_removesOldUserControls() =
         testScope.runTest {
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val mediaLoaded = MediaDataLoadingModel.Loaded(dataMain.instanceId)
 
             // GIVEN that we have a media loaded for main user
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
 
-            assertThat(sortedMedia?.values)
-                .containsExactly(MediaCommonModel.MediaControl(mediaLoaded))
+            assertThat(currentMedia).containsExactly(MediaCommonModel.MediaControl(mediaLoaded))
 
             // and we switch to guest user
             setUser(USER_GUEST)
 
             // THEN we should remove the main user's media
-            verify(listener).onMediaDataRemoved(eq(KEY))
-            assertThat(sortedMedia).isEmpty()
+            verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
+            assertThat(currentMedia).isEmpty()
         }
 
     @Test
     fun onUserSwitched_addsNewUserControls() =
         testScope.runTest {
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val guestLoadedStatesModel = MediaDataLoadingModel.Loaded(dataGuest.instanceId)
             val mainLoadedStatesModel = MediaDataLoadingModel.Loaded(dataMain.instanceId)
 
@@ -272,16 +271,16 @@
                     anyInt(),
                     anyBoolean()
                 )
-            assertThat(sortedMedia?.values)
+            assertThat(currentMedia)
                 .containsExactly(MediaCommonModel.MediaControl(guestLoadedStatesModel))
-            assertThat(sortedMedia?.values)
+            assertThat(currentMedia)
                 .doesNotContain(MediaCommonModel.MediaControl(mainLoadedStatesModel))
         }
 
     @Test
     fun onProfileChanged_profileUnavailable_updateStates() =
         testScope.runTest {
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
 
             // GIVEN that we had some media for both profiles
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
@@ -292,8 +291,8 @@
 
             val mediaLoadedStatesModel = MediaDataLoadingModel.Loaded(dataMain.instanceId)
             // THEN we should remove the private profile media
-            verify(listener).onMediaDataRemoved(eq(KEY_ALT))
-            assertThat(sortedMedia?.values)
+            verify(listener).onMediaDataRemoved(eq(KEY_ALT), eq(false))
+            assertThat(currentMedia)
                 .containsExactly(MediaCommonModel.MediaControl(mediaLoadedStatesModel))
         }
 
@@ -503,7 +502,7 @@
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
 
             mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
-            mediaDataFilter.onMediaDataRemoved(KEY)
+            mediaDataFilter.onMediaDataRemoved(KEY, false)
             assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData))
                 .isFalse()
             assertThat(hasAnyMedia(selectedUserEntries)).isFalse()
@@ -523,13 +522,13 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val recommendationsLoadingModel =
                 SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY, isPrioritized = true)
 
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
-            assertThat(sortedMedia?.values)
+            assertThat(currentMedia)
                 .containsExactly(MediaCommonModel.MediaRecommendations(recommendationsLoadingModel))
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -552,13 +551,13 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
 
             whenever(smartspaceData.isActive).thenReturn(false)
 
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
-            assertThat(sortedMedia).isEmpty()
+            assertThat(currentMedia).isEmpty()
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
@@ -581,7 +580,7 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val recsCommonModel =
                 MediaCommonModel.MediaRecommendations(
                     SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY, isPrioritized = true)
@@ -596,7 +595,7 @@
             clock.advanceTime(MediaDataFilterImpl.SMARTSPACE_MAX_AGE + 100)
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
-            assertThat(sortedMedia?.values).containsExactly(recsCommonModel, controlCommonModel)
+            assertThat(currentMedia).containsExactly(recsCommonModel, controlCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
@@ -618,7 +617,7 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             whenever(smartspaceData.isActive).thenReturn(false)
 
             val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
@@ -626,7 +625,7 @@
             clock.advanceTime(MediaDataFilterImpl.SMARTSPACE_MAX_AGE + 100)
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
-            assertThat(sortedMedia?.values)
+            assertThat(currentMedia)
                 .doesNotContain(
                     MediaCommonModel.MediaRecommendations(
                         SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
@@ -652,7 +651,7 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
 
             whenever(smartspaceData.isActive).thenReturn(false)
 
@@ -665,7 +664,7 @@
                 )
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
 
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel)
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
 
@@ -673,7 +672,7 @@
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
             // THEN we should treat the media as not active instead
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
@@ -696,7 +695,7 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             whenever(smartspaceData.isValid()).thenReturn(false)
 
             // WHEN we have media that was recently played, but not currently active
@@ -707,7 +706,7 @@
                     true
                 )
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel)
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
 
@@ -717,7 +716,7 @@
 
             // THEN we should treat the media as active instead
             val dataCurrentAndActive = dataCurrent.copy(active = true)
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
@@ -747,7 +746,7 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             // WHEN we have media that was recently played, but not currently active
             val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             val controlCommonModel =
@@ -762,7 +761,7 @@
 
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
 
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel)
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
 
@@ -790,7 +789,7 @@
                 )
                 .isTrue()
             // Smartspace update should also be propagated but not prioritized.
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel, recsCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
             verify(listener)
                 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
             verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
@@ -803,13 +802,13 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
 
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
             mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
 
             verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
-            assertThat(sortedMedia?.values).isEmpty()
+            assertThat(currentMedia).isEmpty()
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
@@ -827,7 +826,7 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val controlCommonModel =
                 MediaCommonModel.MediaControl(
                     MediaDataLoadingModel.Loaded(dataMain.instanceId),
@@ -836,7 +835,7 @@
             val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
 
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel)
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
 
@@ -857,7 +856,7 @@
             mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
 
             verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
@@ -875,7 +874,7 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val recsCommonModel =
                 MediaCommonModel.MediaRecommendations(
                     SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
@@ -887,7 +886,7 @@
 
             verify(listener)
                 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
-            assertThat(sortedMedia?.values).containsExactly(recsCommonModel)
+            assertThat(currentMedia).containsExactly(recsCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
@@ -906,7 +905,7 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val recsCommonModel =
                 MediaCommonModel.MediaRecommendations(
                     SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
@@ -926,7 +925,7 @@
 
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel)
 
             // And an inactive recommendation is loaded
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -936,7 +935,7 @@
                 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
             verify(listener, never())
                 .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel, recsCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
@@ -974,7 +973,7 @@
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
             val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val recsCommonModel =
                 MediaCommonModel.MediaRecommendations(
                     SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
@@ -990,7 +989,7 @@
 
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel)
 
             // AND we get a smartspace signal with extra to trigger resume
             runCurrent()
@@ -1009,7 +1008,7 @@
                     eq(100),
                     eq(true)
                 )
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel, recsCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
@@ -1026,7 +1025,7 @@
     @Test
     fun smartspaceLoaded_notShouldTriggerResume_doesNotTrigger() =
         testScope.runTest {
-            val sortedMedia by collectLastValue(repository.sortedMedia)
+            val currentMedia by collectLastValue(repository.currentMedia)
             val recsCommonModel =
                 MediaCommonModel.MediaRecommendations(
                     SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
@@ -1043,7 +1042,7 @@
 
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel)
 
             // AND we get a smartspace signal with extra to not trigger resume
             val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, false) }
@@ -1056,7 +1055,7 @@
             // But the smartspace update is still propagated
             verify(listener)
                 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
-            assertThat(sortedMedia?.values).containsExactly(controlCommonModel, recsCommonModel)
+            assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
         }
 
     private fun hasActiveMediaOrRecommendation(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 1de7ee3..3bf4173 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -71,10 +71,6 @@
 import com.android.systemui.statusbar.SbnBuilder
 import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.time.FakeSystemClock
 import com.android.systemui.utils.os.FakeHandler
@@ -101,6 +97,10 @@
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoSession
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
 
 private const val KEY = "KEY"
@@ -384,7 +384,7 @@
         // THEN it is removed and listeners are informed
         foregroundExecutor.advanceClockToLast()
         foregroundExecutor.runAllReady()
-        verify(listener).onMediaDataRemoved(PACKAGE_NAME)
+        verify(listener).onMediaDataRemoved(PACKAGE_NAME, false)
     }
 
     @Test
@@ -567,7 +567,7 @@
         addNotificationAndLoad()
         val data = mediaDataCaptor.value
         mediaDataProcessor.onNotificationRemoved(KEY)
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
@@ -812,7 +812,7 @@
                 eq(false)
             )
         assertThat(mediaDataCaptor.value.resumption).isTrue()
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         // WHEN the second is removed
         mediaDataProcessor.onNotificationRemoved(KEY_2)
         // THEN the data is for resumption and the second key is removed
@@ -826,7 +826,7 @@
                 eq(false)
             )
         assertThat(mediaDataCaptor.value.resumption).isTrue()
-        verify(listener).onMediaDataRemoved(eq(KEY_2))
+        verify(listener).onMediaDataRemoved(eq(KEY_2), eq(false))
     }
 
     @Test
@@ -851,7 +851,7 @@
         mediaDataProcessor.onNotificationRemoved(KEY)
 
         // THEN the media data is removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -901,7 +901,7 @@
         mediaDataProcessor.onNotificationRemoved(KEY)
 
         // THEN the media data is removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -940,7 +940,7 @@
         assertThat(mediaDataCaptor.value.isPlaying).isFalse()
 
         // And the oldest resume control was removed
-        verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"))
+        verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"), eq(false))
     }
 
     fun testOnNotificationRemoved_lockDownMode() {
@@ -950,7 +950,7 @@
         val data = mediaDataCaptor.value
         mediaDataProcessor.onNotificationRemoved(KEY)
 
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger, never())
             .logActiveConvertedToResume(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
@@ -1183,7 +1183,7 @@
         mediaDataProcessor.setMediaResumptionEnabled(false)
 
         // THEN the resume controls are dismissed
-        verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME))
+        verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
@@ -1191,19 +1191,19 @@
     fun testDismissMedia_listenerCalled() {
         addNotificationAndLoad()
         val data = mediaDataCaptor.value
-        val removed = mediaDataProcessor.dismissMediaData(KEY, 0L)
+        val removed = mediaDataProcessor.dismissMediaData(KEY, 0L, true)
         assertThat(removed).isTrue()
 
         foregroundExecutor.advanceClockToLast()
         foregroundExecutor.runAllReady()
 
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
     @Test
     fun testDismissMedia_keyDoesNotExist_returnsFalse() {
-        val removed = mediaDataProcessor.dismissMediaData(KEY, 0L)
+        val removed = mediaDataProcessor.dismissMediaData(KEY, 0L, true)
         assertThat(removed).isFalse()
     }
 
@@ -2102,7 +2102,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It remains as a regular player
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
     }
@@ -2118,7 +2118,7 @@
         mediaDataProcessor.onNotificationRemoved(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2171,7 +2171,7 @@
         mediaDataProcessor.onNotificationRemoved(KEY)
 
         // It remains as a regular player
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
     }
@@ -2224,7 +2224,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2278,7 +2278,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed.
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(
@@ -2304,7 +2304,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2354,7 +2354,7 @@
         mediaDataProcessor.onNotificationRemoved(KEY)
 
         // We still make sure to remove it
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index a447e44..d2701dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -51,7 +51,6 @@
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
@@ -60,6 +59,7 @@
 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.any
@@ -71,6 +71,7 @@
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.eq
 
 private const val KEY = "TEST_KEY"
 private const val KEY_OLD = "TEST_KEY_OLD"
@@ -158,7 +159,8 @@
 
     @Test
     fun removeUnknown() {
-        manager.onMediaDataRemoved("unknown")
+        manager.onMediaDataRemoved("unknown", false)
+        verify(listener, never()).onKeyRemoved(eq(KEY), anyBoolean())
     }
 
     @Test
@@ -170,7 +172,7 @@
     @Test
     fun loadAndRemoveMediaData() {
         manager.onMediaDataLoaded(KEY, null, mediaData)
-        manager.onMediaDataRemoved(KEY)
+        manager.onMediaDataRemoved(KEY, false)
         fakeBgExecutor.runAllReady()
         verify(lmm).unregisterCallback(any())
         verify(muteAwaitManager).stopListening()
@@ -386,9 +388,9 @@
     fun listenerReceivesKeyRemoved() {
         manager.onMediaDataLoaded(KEY, null, mediaData)
         // WHEN the notification is removed
-        manager.onMediaDataRemoved(KEY)
+        manager.onMediaDataRemoved(KEY, true)
         // THEN the listener receives key removed event
-        verify(listener).onKeyRemoved(eq(KEY))
+        verify(listener).onKeyRemoved(eq(KEY), eq(true))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
index 5a3c220..31a2435 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.media.controls.MediaTestUtils
 import com.android.systemui.media.controls.shared.model.MediaData
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import org.junit.After
 import org.junit.Before
@@ -38,12 +37,13 @@
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
-import org.mockito.Mockito.any
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
 
 private const val PACKAGE = "PKG"
 private const val KEY = "TEST_KEY"
@@ -165,10 +165,10 @@
 
     @Test
     fun noMediaSession_removedEventNotFiltered() {
-        filter.onMediaDataRemoved(KEY)
+        filter.onMediaDataRemoved(KEY, false)
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
-        verify(mediaListener).onMediaDataRemoved(eq(KEY))
+        verify(mediaListener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -193,11 +193,11 @@
         whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
         sessionListener.onActiveSessionsChanged(controllers)
         // WHEN a removed event is received
-        filter.onMediaDataRemoved(KEY)
+        filter.onMediaDataRemoved(KEY, false)
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is not filtered
-        verify(mediaListener).onMediaDataRemoved(eq(KEY))
+        verify(mediaListener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -294,7 +294,7 @@
                 anyBoolean()
             )
         // AND there should be a removed event for key2
-        verify(mediaListener).onMediaDataRemoved(eq(key2))
+        verify(mediaListener).onMediaDataRemoved(eq(key2), eq(false))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index 3cc65c9..6ca0bef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -31,10 +31,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -52,6 +48,10 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
 
 private const val KEY = "KEY"
 private const val PACKAGE = "PKG"
@@ -166,12 +166,12 @@
     @Test
     fun testOnMediaDataRemoved_unregistersPlaybackListener() {
         mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
-        mediaTimeoutListener.onMediaDataRemoved(KEY)
+        mediaTimeoutListener.onMediaDataRemoved(KEY, false)
         verify(mediaController).unregisterCallback(anyObject())
 
         // Ignores duplicate requests
         clearInvocations(mediaController)
-        mediaTimeoutListener.onMediaDataRemoved(KEY)
+        mediaTimeoutListener.onMediaDataRemoved(KEY, false)
         verify(mediaController, never()).unregisterCallback(anyObject())
     }
 
@@ -181,7 +181,7 @@
         mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
         assertThat(executor.numPending()).isEqualTo(1)
         // WHEN the media is removed
-        mediaTimeoutListener.onMediaDataRemoved(KEY)
+        mediaTimeoutListener.onMediaDataRemoved(KEY, false)
         // THEN the timeout runnable is cancelled
         assertThat(executor.numPending()).isEqualTo(0)
     }
@@ -398,7 +398,7 @@
         // WHEN we have a resume control
         testOnMediaDataLoaded_resumption_registersTimeout()
         // AND the media is removed
-        mediaTimeoutListener.onMediaDataRemoved(PACKAGE)
+        mediaTimeoutListener.onMediaDataRemoved(PACKAGE, false)
 
         // THEN the timeout runnable is cancelled
         assertThat(executor.numPending()).isEqualTo(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 0a5aace..7856f9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -33,6 +33,10 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -52,15 +56,16 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.qs.PageIndicator
 import com.android.systemui.res.R
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.settings.GlobalSettings
 import com.android.systemui.util.settings.SecureSettings
@@ -74,12 +79,14 @@
 import kotlinx.coroutines.test.TestDispatcher
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.anyLong
 import org.mockito.Mockito.floatThat
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
@@ -88,6 +95,9 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
 
 private val DATA = MediaTestUtils.emptyMediaData
 
@@ -136,6 +146,9 @@
     private lateinit var testDispatcher: TestDispatcher
     private lateinit var mediaCarouselController: MediaCarouselController
 
+    private var originalResumeSetting =
+        Settings.Secure.getInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 1)
+
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
@@ -145,29 +158,30 @@
         testDispatcher = UnconfinedTestDispatcher()
         mediaCarouselController =
             MediaCarouselController(
-                context,
-                mediaControlPanelFactory,
-                visualStabilityProvider,
-                mediaHostStatesManager,
-                activityStarter,
-                clock,
-                kosmos.testDispatcher,
-                executor,
-                bgExecutor,
-                testDispatcher,
-                mediaDataManager,
-                configurationController,
-                falsingManager,
-                dumpManager,
-                logger,
-                debugLogger,
-                mediaFlags,
-                keyguardUpdateMonitor,
-                kosmos.keyguardTransitionInteractor,
-                globalSettings,
-                secureSettings,
-                kosmos.mediaCarouselViewModel,
-                mediaViewControllerFactory,
+                context = context,
+                mediaControlPanelFactory = mediaControlPanelFactory,
+                visualStabilityProvider = visualStabilityProvider,
+                mediaHostStatesManager = mediaHostStatesManager,
+                activityStarter = activityStarter,
+                systemClock = clock,
+                mainDispatcher = kosmos.testDispatcher,
+                executor = executor,
+                bgExecutor = bgExecutor,
+                backgroundDispatcher = testDispatcher,
+                mediaManager = mediaDataManager,
+                configurationController = configurationController,
+                falsingManager = falsingManager,
+                dumpManager = dumpManager,
+                logger = logger,
+                debugLogger = debugLogger,
+                mediaFlags = mediaFlags,
+                keyguardUpdateMonitor = keyguardUpdateMonitor,
+                keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
+                globalSettings = globalSettings,
+                secureSettings = secureSettings,
+                mediaCarouselViewModel = kosmos.mediaCarouselViewModel,
+                mediaViewControllerFactory = mediaViewControllerFactory,
+                sceneInteractor = kosmos.sceneInteractor,
             )
         verify(configurationController).addCallback(capture(configListener))
         verify(mediaDataManager).addListener(capture(listener))
@@ -186,6 +200,15 @@
             )
     }
 
+    @After
+    fun tearDown() {
+        Settings.Secure.putInt(
+            context.contentResolver,
+            Settings.Secure.MEDIA_CONTROLS_RESUME,
+            originalResumeSetting
+        )
+    }
+
     @Test
     fun testPlayerOrdering() {
         // Test values: key, data, last active time
@@ -818,10 +841,12 @@
         verify(mediaCarousel).visibility = View.VISIBLE
     }
 
+    @DisableSceneContainer
     @ExperimentalCoroutinesApi
     @Test
     fun testKeyguardGone_showMediaCarousel() =
         kosmos.testScope.runTest {
+            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
             var updatedVisibility = false
             mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
             mediaCarouselController.mediaCarousel = mediaCarousel
@@ -840,10 +865,30 @@
             job.cancel()
         }
 
+    @EnableSceneContainer
+    @ExperimentalCoroutinesApi
+    @Test
+    fun testKeyguardGone_showMediaCarousel_scene_container() =
+        kosmos.testScope.runTest {
+            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
+            var updatedVisibility = false
+            mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
+            mediaCarouselController.mediaCarousel = mediaCarousel
+
+            val job = mediaCarouselController.listenForAnyStateToGoneKeyguardTransition(this)
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+            verify(mediaCarousel).visibility = View.VISIBLE
+            assertEquals(true, updatedVisibility)
+
+            job.cancel()
+        }
+
     @ExperimentalCoroutinesApi
     @Test
     fun keyguardShowing_notAllowedOnLockscreen_updateVisibility() {
         kosmos.testScope.runTest {
+            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
             var updatedVisibility = false
             mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
             mediaCarouselController.mediaCarousel = mediaCarousel
@@ -870,6 +915,7 @@
     @Test
     fun keyguardShowing_allowedOnLockscreen_updateVisibility() {
         kosmos.testScope.runTest {
+            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
             var updatedVisibility = false
             mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
             mediaCarouselController.mediaCarousel = mediaCarousel
@@ -968,6 +1014,45 @@
         verify(panel).updateAnimatorDurationScale()
     }
 
+    @Test
+    fun swipeToDismiss_pausedAndResumeOff_userInitiated() {
+        // When resumption is disabled, paused media should be dismissed after being swiped away
+        Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
+
+        val pausedMedia = DATA.copy(isPlaying = false)
+        listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, pausedMedia)
+        mediaCarouselController.onSwipeToDismiss()
+
+        // When it can be removed immediately on update
+        whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(true)
+        val inactiveMedia = pausedMedia.copy(active = false)
+        listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, inactiveMedia)
+
+        // This is processed as a user-initiated dismissal
+        verify(debugLogger).logMediaRemoved(eq(PAUSED_LOCAL), eq(true))
+        verify(mediaDataManager).dismissMediaData(eq(PAUSED_LOCAL), anyLong(), eq(true))
+    }
+
+    @Test
+    fun swipeToDismiss_pausedAndResumeOff_delayed_userInitiated() {
+        // When resumption is disabled, paused media should be dismissed after being swiped away
+        Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
+        mediaCarouselController.updateHostVisibility = {}
+
+        val pausedMedia = DATA.copy(isPlaying = false)
+        listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, pausedMedia)
+        mediaCarouselController.onSwipeToDismiss()
+
+        // When it can't be removed immediately on update
+        whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(false)
+        val inactiveMedia = pausedMedia.copy(active = false)
+        listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, inactiveMedia)
+        visualStabilityCallback.value.onReorderingAllowed()
+
+        // This is processed as a user-initiated dismissal
+        verify(mediaDataManager).dismissMediaData(eq(PAUSED_LOCAL), anyLong(), eq(true))
+    }
+
     /**
      * Helper method when a configuration change occurs.
      *
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 83e4d31..6d7976e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -98,11 +98,6 @@
 import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.KotlinArgumentCaptor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.withArgCaptor
 import com.android.systemui.util.settings.GlobalSettings
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -125,6 +120,9 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
 
 private const val KEY = "TEST_KEY"
 private const val PACKAGE = "PKG"
@@ -247,8 +245,7 @@
         // Set up package manager mocks
         val icon = context.getDrawable(R.drawable.ic_android)
         whenever(packageManager.getApplicationIcon(anyString())).thenReturn(icon)
-        whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java)))
-            .thenReturn(icon)
+        whenever(packageManager.getApplicationIcon(any<ApplicationInfo>())).thenReturn(icon)
         whenever(packageManager.getApplicationInfo(eq(PACKAGE), anyInt()))
             .thenReturn(applicationInfo)
         whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE)
@@ -644,7 +641,7 @@
         bgExecutor.runAllReady()
         mainExecutor.runAllReady()
 
-        verify(albumView).setImageDrawable(any(Drawable::class.java))
+        verify(albumView).setImageDrawable(any<Drawable>())
     }
 
     @Test
@@ -657,7 +654,7 @@
         bgExecutor.runAllReady()
         mainExecutor.runAllReady()
 
-        verify(albumView).setImageDrawable(any(Drawable::class.java))
+        verify(albumView).setImageDrawable(any<Drawable>())
     }
 
     @Test
@@ -675,12 +672,12 @@
         player.bindPlayer(state0, PACKAGE)
         bgExecutor.runAllReady()
         mainExecutor.runAllReady()
-        verify(albumView).setImageDrawable(any(Drawable::class.java))
+        verify(albumView).setImageDrawable(any<Drawable>())
 
         // Run Metadata update so that later states don't update
         val captor = argumentCaptor<Animator.AnimatorListener>()
         verify(mockAnimator, times(2)).addListener(captor.capture())
-        captor.value.onAnimationEnd(mockAnimator)
+        captor.lastValue.onAnimationEnd(mockAnimator)
         assertThat(titleText.getText()).isEqualTo(TITLE)
         assertThat(artistText.getText()).isEqualTo(ARTIST)
 
@@ -696,13 +693,13 @@
         player.bindPlayer(state2, PACKAGE)
         bgExecutor.runAllReady()
         mainExecutor.runAllReady()
-        verify(albumView, times(2)).setImageDrawable(any(Drawable::class.java))
+        verify(albumView, times(2)).setImageDrawable(any<Drawable>())
 
         // Fourth binding to new image runs transition due to color scheme change
         player.bindPlayer(state3, PACKAGE)
         bgExecutor.runAllReady()
         mainExecutor.runAllReady()
-        verify(albumView, times(3)).setImageDrawable(any(Drawable::class.java))
+        verify(albumView, times(3)).setImageDrawable(any<Drawable>())
     }
 
     @Test
@@ -974,7 +971,7 @@
 
         val captor = argumentCaptor<SeekBarObserver>()
         verify(seekBarData).observeForever(captor.capture())
-        val seekBarObserver = captor.value!!
+        val seekBarObserver = captor.lastValue
 
         // Then the seekbar is set to animate
         assertThat(seekBarObserver.animationEnabled).isTrue()
@@ -1086,27 +1083,19 @@
         whenever(mockAvd0.isRunning()).thenReturn(false)
         val captor = ArgumentCaptor.forClass(Animatable2.AnimationCallback::class.java)
         verify(mockAvd0, times(1)).registerAnimationCallback(captor.capture())
-        verify(mockAvd1, never())
-            .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
-        verify(mockAvd2, never())
-            .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+        verify(mockAvd1, never()).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+        verify(mockAvd2, never()).registerAnimationCallback(any<Animatable2.AnimationCallback>())
         captor.getValue().onAnimationEnd(mockAvd0)
 
         // Validate correct state was bound
         assertThat(actionPlayPause.contentDescription).isEqualTo("loading")
         assertThat(actionPlayPause.getBackground()).isNull()
-        verify(mockAvd0, times(1))
-            .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
-        verify(mockAvd1, times(1))
-            .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
-        verify(mockAvd2, times(1))
-            .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
-        verify(mockAvd0, times(1))
-            .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
-        verify(mockAvd1, times(1))
-            .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
-        verify(mockAvd2, never())
-            .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+        verify(mockAvd0, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+        verify(mockAvd1, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+        verify(mockAvd2, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+        verify(mockAvd0, times(1)).unregisterAnimationCallback(any<Animatable2.AnimationCallback>())
+        verify(mockAvd1, times(1)).unregisterAnimationCallback(any<Animatable2.AnimationCallback>())
+        verify(mockAvd2, never()).unregisterAnimationCallback(any<Animatable2.AnimationCallback>())
     }
 
     @Test
@@ -1118,7 +1107,7 @@
         // Capture animation handler
         val captor = argumentCaptor<Animator.AnimatorListener>()
         verify(mockAnimator, times(2)).addListener(captor.capture())
-        val handler = captor.value
+        val handler = captor.lastValue
 
         // Validate text views unchanged but animation started
         assertThat(titleText.getText()).isEqualTo("")
@@ -1147,7 +1136,7 @@
         // Capture animation handler
         val captor = argumentCaptor<Animator.AnimatorListener>()
         verify(mockAnimator, times(2)).addListener(captor.capture())
-        val handler = captor.value
+        val handler = captor.lastValue
 
         // Validate text views unchanged but animation started
         assertThat(titleText.getText()).isEqualTo("")
@@ -1179,7 +1168,7 @@
         // Capture animation handler
         val captor = argumentCaptor<Animator.AnimatorListener>()
         verify(mockAnimator, times(2)).addListener(captor.capture())
-        val handler = captor.value
+        val handler = captor.lastValue
 
         handler.onAnimationEnd(mockAnimator)
         assertThat(artistText.getText()).isEqualTo("ARTIST_0")
@@ -1344,7 +1333,7 @@
         assertThat(dismiss.isEnabled).isEqualTo(true)
         dismiss.callOnClick()
         verify(logger).logLongPressDismiss(anyInt(), eq(PACKAGE), eq(instanceId))
-        verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
+        verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong(), eq(true))
     }
 
     @Test
@@ -1360,7 +1349,8 @@
     @Test
     fun player_dismissButtonClick_notInManager() {
         val mediaKey = "key for dismissal"
-        whenever(mediaDataManager.dismissMediaData(eq(mediaKey), anyLong())).thenReturn(false)
+        whenever(mediaDataManager.dismissMediaData(eq(mediaKey), anyLong(), eq(true)))
+            .thenReturn(false)
 
         player.attachPlayer(viewHolder)
         val state = mediaData.copy(notificationKey = KEY)
@@ -1369,8 +1359,8 @@
         assertThat(dismiss.isEnabled).isEqualTo(true)
         dismiss.callOnClick()
 
-        verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
-        verify(mediaCarouselController).removePlayer(eq(mediaKey), eq(false), eq(false))
+        verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong(), eq(true))
+        verify(mediaCarouselController).removePlayer(eq(mediaKey), eq(false), eq(false), eq(true))
     }
 
     @Test
@@ -1774,10 +1764,9 @@
         player.attachPlayer(viewHolder)
         player.bindPlayer(mediaData, KEY)
 
-        val callback: () -> Unit = {}
-        val captor = KotlinArgumentCaptor(callback::class.java)
+        val captor = argumentCaptor<() -> Unit>()
         verify(seekBarViewModel).logSeek = captor.capture()
-        captor.value.invoke()
+        captor.lastValue.invoke()
 
         verify(logger).logSeek(anyInt(), eq(PACKAGE), eq(instanceId))
     }
@@ -1800,7 +1789,7 @@
         // THEN it sends the PendingIntent without dismissing keyguard first,
         // and does not use the Intent directly (see b/271845008)
         captor.value.onClick(viewHolder.player)
-        verify(pendingIntent).send(any(Bundle::class.java))
+        verify(pendingIntent).send(any<Bundle>())
         verify(pendingIntent, never()).getIntent()
         verify(activityStarter, never()).postStartActivityDismissingKeyguard(eq(clickIntent), any())
     }
@@ -2218,8 +2207,8 @@
         mainExecutor.runAllReady()
 
         verify(recCardTitle).setTextColor(any<Int>())
-        verify(recAppIconItem, times(3)).setImageDrawable(any(Drawable::class.java))
-        verify(coverItem, times(3)).setImageDrawable(any(Drawable::class.java))
+        verify(recAppIconItem, times(3)).setImageDrawable(any<Drawable>())
+        verify(coverItem, times(3)).setImageDrawable(any<Drawable>())
         verify(coverItem, times(3)).imageMatrix = any()
     }
 
@@ -2546,7 +2535,7 @@
         seamless.callOnClick()
 
         // Then we send the pending intent as is, without modifying the original intent
-        verify(pendingIntent).send(any(Bundle::class.java))
+        verify(pendingIntent).send(any<Bundle>())
         verify(pendingIntent, never()).getIntent()
     }
 
@@ -2578,13 +2567,16 @@
         return Icon.createWithBitmap(bmp)
     }
 
-    private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener =
-        withArgCaptor {
-            verify(seekBarViewModel).setScrubbingChangeListener(capture())
-        }
+    private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener {
+        val captor = argumentCaptor<SeekBarViewModel.ScrubbingChangeListener>()
+        verify(seekBarViewModel).setScrubbingChangeListener(captor.capture())
+        return captor.lastValue
+    }
 
-    private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener = withArgCaptor {
-        verify(seekBarViewModel).setEnabledChangeListener(capture())
+    private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener {
+        val captor = argumentCaptor<SeekBarViewModel.EnabledChangeListener>()
+        verify(seekBarViewModel).setEnabledChangeListener(captor.capture())
+        return captor.lastValue
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
index 3b6a88a..5dbfe47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
@@ -25,6 +25,8 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.Intent;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.filters.SmallTest;
@@ -91,8 +93,8 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
     public void launchMediaOutputBroadcastDialog_flagOff_broadcastDialogFactoryNotCalled() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING);
         Intent intent = new Intent(
                 MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
         intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName());
@@ -105,8 +107,8 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
     public void launchMediaOutputBroadcastDialog_ExtraPackageName_BroadcastDialogFactoryCalled() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING);
         Intent intent = new Intent(
                 MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
         intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName());
@@ -119,8 +121,8 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
     public void launchMediaOutputBroadcastDialog_WrongExtraKey_DialogBroadcastFactoryNotCalled() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING);
         Intent intent = new Intent(
                 MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
         intent.putExtra("Wrong Package Name Key", getContext().getPackageName());
@@ -133,8 +135,8 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
     public void launchMediaOutputBroadcastDialog_NoExtra_BroadcastDialogFactoryNotCalled() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING);
         Intent intent = new Intent(
                 MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
         mMediaOutputDialogReceiver.onReceive(getContext(), intent);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index ff7c970..8f8630e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -104,11 +104,11 @@
         listener.onMediaDataLoaded(mKey, mOldKey, mData, /* immediately= */true,
                 /* receivedSmartspaceCardLatency= */0, /* isSsReactived= */ false);
 
-        listener.onMediaDataRemoved(mKey);
+        listener.onMediaDataRemoved(mKey, false);
         verify(mDreamOverlayStateController, never()).removeComplication(any());
 
         when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
-        listener.onMediaDataRemoved(mKey);
+        listener.onMediaDataRemoved(mKey, false);
         verify(mDreamOverlayStateController).removeComplication(eq(mMediaEntryComplication));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
index db275ec..db36131 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
@@ -52,7 +52,7 @@
             val taskId = 123
             val isLowResolution = false
             val snapshot = createTaskSnapshot()
-            val thumbnailData = ThumbnailData(snapshot)
+            val thumbnailData = ThumbnailData.fromSnapshot(snapshot)
             whenever(activityManager.getTaskThumbnail(taskId, isLowResolution))
                 .thenReturn(thumbnailData)
 
@@ -74,7 +74,7 @@
     fun captureThumbnail_thumbnailAvailable_returnsThumbnailData() =
         testScope.runTest {
             val taskId = 321
-            val thumbnailData = ThumbnailData(createTaskSnapshot())
+            val thumbnailData = ThumbnailData.fromSnapshot(createTaskSnapshot())
 
             whenever(activityManager.takeTaskThumbnail(taskId)).thenReturn(thumbnailData)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
index 8e05410..c06a28e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
@@ -42,13 +42,13 @@
     fun updateFlags() {
         underTest.updateFlags(
             Display.DEFAULT_DISPLAY,
-            1 to true,
-            2 to false,
-            3 to true,
+            1L to true,
+            2L to false,
+            3L to true,
         )
 
-        assertThat(underTest.flags and 1).isNotEqualTo(0)
-        assertThat(underTest.flags and 2).isEqualTo(0)
-        assertThat(underTest.flags and 3).isNotEqualTo(0)
+        assertThat(underTest.flags and 1L).isNotEqualTo(0L)
+        assertThat(underTest.flags and 2L).isEqualTo(0L)
+        assertThat(underTest.flags and 3L).isNotEqualTo(0L)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
index 9f0e67b..85cc88d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
@@ -15,11 +15,13 @@
  */
 package com.android.systemui.monet
 
-import androidx.test.filters.SmallTest
 import android.testing.AndroidTestingRunner
 import android.util.Log
+import android.util.Pair
+import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.theme.DynamicColors
+import com.google.ux.material.libmonet.dynamiccolor.DynamicColor
 import com.google.ux.material.libmonet.hct.Hct
 import com.google.ux.material.libmonet.scheme.SchemeTonalSpot
 import java.io.File
@@ -81,6 +83,10 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class ColorSchemeTest : SysuiTestCase() {
+    private val defaultContrast = 0.0
+    private val defaultIsDark = false
+    private val defaultIsFidelity = false
+
     @Test
     fun generateThemeStyles() {
         val document = buildDoc<Any>()
@@ -107,7 +113,7 @@
                 }
 
                 val style = document.createElement(styleValue.name.lowercase())
-                val colorScheme = ColorScheme(sourceColor.toInt(), false, styleValue)
+                val colorScheme = ColorScheme(sourceColor.toInt(), defaultIsDark, styleValue)
 
                 style.appendChild(
                     document.createTextNode(
@@ -139,7 +145,7 @@
         document.appendWithBreak(resources)
 
         // shade colors
-        val colorScheme = ColorScheme(GOOGLE_BLUE, false)
+        val colorScheme = ColorScheme(GOOGLE_BLUE, defaultIsDark)
         arrayOf(
                 Triple("accent1", "Primary", colorScheme.accent1),
                 Triple("accent2", "Secondary", colorScheme.accent2),
@@ -162,24 +168,35 @@
 
         resources.appendWithBreak(document.createComment(commentRoles), 2)
 
-        // dynamic colors
-        arrayOf(false, true).forEach { isDark ->
-            val suffix = if (isDark) "_dark" else "_light"
-            val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, 0.5)
-            DynamicColors.allDynamicColorsMapped(false).forEach {
-                resources.createColorEntry(
-                    "system_${it.first}$suffix",
-                    it.second.getArgb(dynamicScheme)
-                )
+        fun generateDynamic(pairs: List<Pair<String, DynamicColor>>) {
+            arrayOf(false, true).forEach { isDark ->
+                val suffix = if (isDark) "_dark" else "_light"
+                val dynamicScheme =
+                    SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, defaultContrast)
+                pairs.forEach {
+                    resources.createColorEntry(
+                        "system_${it.first}$suffix",
+                        it.second.getArgb(dynamicScheme)
+                    )
+                }
             }
         }
 
+        // dynamic colors
+        generateDynamic(DynamicColors.allDynamicColorsMapped(defaultIsFidelity))
+
         // fixed colors
-        val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), false, 0.5)
-        DynamicColors.getFixedColorsMapped(false).forEach {
+        val dynamicScheme =
+            SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), defaultIsDark, defaultContrast)
+        DynamicColors.getFixedColorsMapped(defaultIsFidelity).forEach {
             resources.createColorEntry("system_${it.first}", it.second.getArgb(dynamicScheme))
         }
 
+        resources.appendWithBreak(document.createComment(commentRoles), 2)
+
+        // custom colors
+        generateDynamic(DynamicColors.getCustomColorsMapped(defaultIsFidelity))
+
         saveFile(document, "role_values.xml")
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index 224e755..2ff660f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -125,7 +125,7 @@
     private AccessibilityManager.AccessibilityServicesStateChangeListener
             mAccessibilityServicesStateChangeListener;
 
-    private static final int ACCESSIBILITY_BUTTON_CLICKABLE_STATE =
+    private static final long ACCESSIBILITY_BUTTON_CLICKABLE_STATE =
             SYSUI_STATE_A11Y_BUTTON_CLICKABLE | SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
     private NavBarHelper mNavBarHelper;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 0e7a215..6cea1e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -38,7 +38,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -300,7 +300,7 @@
         doNothing().when(mWindowManager).addView(any(), any());
         doNothing().when(mWindowManager).removeViewImmediate(any());
         mMockSysUiState = mock(SysUiState.class);
-        when(mMockSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mMockSysUiState);
+        when(mMockSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mMockSysUiState);
 
         mContext.addMockSystemService(WindowManager.class, mWindowManager);
         mSysuiTestableContextExternal.addMockSystemService(WindowManager.class, mWindowManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
index 8d01e80d..bba275e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
@@ -16,18 +16,18 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.wm.shell.back.BackAnimation
 import com.android.wm.shell.pip.Pip
+import java.util.Optional
 import org.junit.Before
 import org.junit.Test
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.any
 import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
-import java.util.Optional
 
 @SmallTest
 class TaskbarDelegateTest : SysuiTestCase() {
@@ -74,7 +74,7 @@
         `when`(mNavBarHelper.edgeBackGestureHandler).thenReturn(mEdgeBackGestureHandler)
         `when`(mLightBarControllerFactory.create(any())).thenReturn(mLightBarTransitionController)
         `when`(mNavBarHelper.currentSysuiState).thenReturn(mCurrentSysUiState)
-        `when`(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState)
+        `when`(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState)
         mTaskStackChangeListeners = TaskStackChangeListeners.getTestInstance()
         mTaskbarDelegate = TaskbarDelegate(context, mLightBarControllerFactory,
             mStatusBarKeyguardViewManager)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 890e1e0..0998c0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -94,6 +94,8 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
@@ -1576,17 +1578,19 @@
     }
 
     @Test
+    @DisableFlags({
+        android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS,
+        android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL
+    })
     public void testUpdateGeneratedPreview_flagDisabled() {
-        mSetFlagsRule.disableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
-        mSetFlagsRule.disableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
         mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle());
         verify(mAppWidgetManager, times(0)).setWidgetPreview(any(), anyInt(), any());
     }
 
     @Test
+    @EnableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS)
+    @DisableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL)
     public void testUpdateGeneratedPreview_userLocked() {
-        mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
-        mSetFlagsRule.disableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
         when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(false);
 
         mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle());
@@ -1594,9 +1598,9 @@
     }
 
     @Test
+    @EnableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS)
+    @DisableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL)
     public void testUpdateGeneratedPreview_userUnlocked() {
-        mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
-        mSetFlagsRule.disableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
         when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true);
         when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true);
 
@@ -1605,9 +1609,9 @@
     }
 
     @Test
+    @EnableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS)
+    @DisableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL)
     public void testUpdateGeneratedPreview_doesNotSetTwice() {
-        mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
-        mSetFlagsRule.disableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
         when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true);
         when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true);
 
@@ -1617,9 +1621,11 @@
     }
 
     @Test
+    @EnableFlags({
+        android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS,
+        android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL
+    })
     public void testUpdateGeneratedPreviewWithDataParcel_userLocked() throws InterruptedException {
-        mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
-        mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
         when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(false);
 
         mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle());
@@ -1628,10 +1634,12 @@
     }
 
     @Test
+    @EnableFlags({
+        android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS,
+        android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL
+    })
     public void testUpdateGeneratedPreviewWithDataParcel_userUnlocked()
             throws InterruptedException {
-        mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
-        mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
         when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true);
         when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true);
 
@@ -1641,10 +1649,12 @@
     }
 
     @Test
+    @EnableFlags({
+        android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS,
+        android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL
+    })
     public void testUpdateGeneratedPreviewWithDataParcel_doesNotSetTwice()
             throws InterruptedException {
-        mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
-        mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
         when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true);
         when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt
index 629c663..bc947fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt
@@ -3,6 +3,7 @@
 import android.content.ComponentName
 import android.service.quicksettings.Tile
 import android.testing.AndroidTestingRunner
+import android.widget.Switch
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.qs.QSTile
@@ -66,15 +67,19 @@
         assertThat(proto?.hasBooleanState()).isFalse()
     }
 
+    /**
+     * The [QSTile.AdapterState.expandedAccessibilityClassName] setting to [Switch] results in the
+     * proto having a booleanState. The value of that boolean is true iff the tile is active.
+     */
     @Test
-    fun booleanState_ACTIVE() {
+    fun adapterState_ACTIVE() {
         val state =
-            QSTile.BooleanState().apply {
+            QSTile.AdapterState().apply {
                 spec = TEST_SPEC
                 label = TEST_LABEL
                 secondaryLabel = TEST_SUBTITLE
                 state = Tile.STATE_ACTIVE
-                value = true
+                expandedAccessibilityClassName = Switch::class.java.name
             }
         val proto = state.toProto()
 
@@ -89,6 +94,33 @@
         assertThat(proto?.booleanState).isTrue()
     }
 
+    /**
+     * Similar to [adapterState_ACTIVE], the use of
+     * [QSTile.AdapterState.expandedAccessibilityClassName] signals that the tile is toggleable.
+     */
+    @Test
+    fun adapterState_INACTIVE() {
+        val state =
+            QSTile.AdapterState().apply {
+                spec = TEST_SPEC
+                label = TEST_LABEL
+                secondaryLabel = TEST_SUBTITLE
+                state = Tile.STATE_INACTIVE
+                expandedAccessibilityClassName = Switch::class.java.name
+            }
+        val proto = state.toProto()
+
+        assertThat(proto).isNotNull()
+        assertThat(proto?.hasSpec()).isTrue()
+        assertThat(proto?.spec).isEqualTo(TEST_SPEC)
+        assertThat(proto?.hasComponentName()).isFalse()
+        assertThat(proto?.label).isEqualTo(TEST_LABEL)
+        assertThat(proto?.secondaryLabel).isEqualTo(TEST_SUBTITLE)
+        assertThat(proto?.state).isEqualTo(Tile.STATE_INACTIVE)
+        assertThat(proto?.hasBooleanState()).isTrue()
+        assertThat(proto?.booleanState).isFalse()
+    }
+
     @Test
     fun noSpec_returnsNull() {
         val state =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index f57f040..68307b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -16,10 +16,12 @@
 package com.android.systemui.qs.external;
 
 import static android.os.PowerExemptionManager.REASON_TILE_ONCLICK;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
 import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PENDING_INTENT;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -55,13 +57,15 @@
 import android.os.HandlerThread;
 import android.os.IDeviceIdleController;
 import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.service.quicksettings.IQSService;
 import android.service.quicksettings.IQSTileService;
 import android.service.quicksettings.TileService;
 
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -73,12 +77,24 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
 import org.mockito.MockitoSession;
 
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 public class TileLifecycleManagerTest extends SysuiTestCase {
 
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+    }
+
     private final PackageManagerAdapter mMockPackageManagerAdapter =
             mock(PackageManagerAdapter.class);
     private final BroadcastDispatcher mMockBroadcastDispatcher =
@@ -98,6 +114,11 @@
     private TestContextWrapper mWrappedContext;
     private MockitoSession mMockitoSession;
 
+    public TileLifecycleManagerTest(FlagsParameterization flags) {
+        super();
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Before
     public void setUp() throws Exception {
         setPackageEnabled(true);
@@ -263,7 +284,8 @@
     }
 
     @Test
-    public void testNoClickOfNotListeningAnymore() throws Exception {
+    @DisableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
+    public void testNoClickIfNotListeningAnymore() throws Exception {
         mStateManager.onTileAdded();
         mStateManager.onStartListening();
         mStateManager.onClick(null);
@@ -279,6 +301,42 @@
     }
 
     @Test
+    @EnableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
+    public void testNoClickIfNotListeningBeforeClick() throws Exception {
+        mStateManager.onTileAdded();
+        mStateManager.onStartListening();
+        mStateManager.onStopListening();
+        mStateManager.onClick(null);
+        mStateManager.executeSetBindService(true);
+        mExecutor.runAllReady();
+
+        verifyBind(1);
+        mStateManager.executeSetBindService(false);
+        mExecutor.runAllReady();
+        assertFalse(mContext.isBound(mTileServiceComponentName));
+        verify(mMockTileService, never()).onClick(null);
+    }
+
+    @Test
+    @EnableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
+    public void testClickIfStopListeningBeforeProcessedClick() throws Exception {
+        mStateManager.onTileAdded();
+        mStateManager.onStartListening();
+        mStateManager.onClick(null);
+        mStateManager.onStopListening();
+        mStateManager.executeSetBindService(true);
+        mExecutor.runAllReady();
+
+        verifyBind(1);
+        mStateManager.executeSetBindService(false);
+        mExecutor.runAllReady();
+        assertFalse(mContext.isBound(mTileServiceComponentName));
+        InOrder inOrder = Mockito.inOrder(mMockTileService);
+        inOrder.verify(mMockTileService).onClick(null);
+        inOrder.verify(mMockTileService).onStopListening();
+    }
+
+    @Test
     public void testComponentEnabling() throws Exception {
         mStateManager.onTileAdded();
         mStateManager.onStartListening();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
index 0ff29db..1c86638 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
@@ -15,12 +15,18 @@
  */
 package com.android.systemui.qs.external;
 
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -32,16 +38,19 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.After;
 import org.junit.Before;
@@ -51,10 +60,20 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 public class TileServiceManagerTest extends SysuiTestCase {
 
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+    }
+
     @Mock
     private TileServices mTileServices;
     @Mock
@@ -68,17 +87,22 @@
     @Mock
     private CustomTileAddedRepository mCustomTileAddedRepository;
 
-    private HandlerThread mThread;
-    private Handler mHandler;
+    private FakeExecutor mFakeExecutor;
+
     private TileServiceManager mTileServiceManager;
     private ComponentName mComponentName;
 
+    public TileServiceManagerTest(FlagsParameterization flags) {
+        super();
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mThread = new HandlerThread("TestThread");
-        mThread.start();
-        mHandler = Handler.createAsync(mThread.getLooper());
+        mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+        Handler handler = mockExecutorHandler(mFakeExecutor);
+
         when(mUserTracker.getUserId()).thenReturn(UserHandle.USER_SYSTEM);
         when(mUserTracker.getUserHandle()).thenReturn(UserHandle.SYSTEM);
 
@@ -90,13 +114,12 @@
         mComponentName = new ComponentName(mContext, TileServiceManagerTest.class);
         when(mTileLifecycle.getComponent()).thenReturn(mComponentName);
 
-        mTileServiceManager = new TileServiceManager(mTileServices, mHandler, mUserTracker,
+        mTileServiceManager = new TileServiceManager(mTileServices, handler, mUserTracker,
                 mCustomTileAddedRepository, mTileLifecycle);
     }
 
     @After
     public void tearDown() throws Exception {
-        mThread.quit();
         mTileServiceManager.handleDestroy();
     }
 
@@ -201,4 +224,59 @@
         verify(mTileLifecycle, times(2)).executeSetBindService(captor.capture());
         assertFalse((boolean) captor.getValue());
     }
+
+    @Test
+    @DisableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
+    public void testStopListeningAndUnbindImmediatelyAfterUpdate() {
+        when(mTileLifecycle.isActiveTile()).thenReturn(true);
+        mTileServiceManager.startLifecycleManagerAndAddTile();
+        mTileServiceManager.setBindAllowed(true);
+        clearInvocations(mTileLifecycle);
+
+        mTileServiceManager.setBindRequested(true);
+        verify(mTileLifecycle).executeSetBindService(true);
+
+        mTileServiceManager.setLastUpdate(0);
+        mFakeExecutor.advanceClockToLast();
+        mFakeExecutor.runAllReady();
+        verify(mTileLifecycle).onStopListening();
+        verify(mTileLifecycle).executeSetBindService(false);
+    }
+
+    @Test
+    @EnableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
+    public void testStopListeningAndUnbindImmediatelyAfterUpdate_ifRequestedFromTileService() {
+        when(mTileLifecycle.isActiveTile()).thenReturn(true);
+        mTileServiceManager.startLifecycleManagerAndAddTile();
+        mTileServiceManager.setBindAllowed(true);
+        clearInvocations(mTileLifecycle);
+
+        mTileServiceManager.setBindRequested(true);
+        mTileServiceManager.onStartListeningFromRequest();
+        verify(mTileLifecycle).onStartListening();
+
+        mTileServiceManager.setLastUpdate(0);
+        mFakeExecutor.advanceClockToLast();
+        mFakeExecutor.runAllReady();
+        verify(mTileLifecycle).onStopListening();
+        verify(mTileLifecycle).executeSetBindService(false);
+    }
+
+    @Test
+    @EnableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
+    public void testNotUnbindImmediatelyAfterUpdate_ifRequestedFromSystemUI() {
+        when(mTileLifecycle.isActiveTile()).thenReturn(true);
+        mTileServiceManager.startLifecycleManagerAndAddTile();
+        mTileServiceManager.setBindAllowed(true);
+        clearInvocations(mTileLifecycle);
+
+        mTileServiceManager.setBindRequested(true);
+        // The tile requests startListening (because a click happened)
+
+        mTileServiceManager.setLastUpdate(0);
+        mFakeExecutor.advanceClockToLast();
+        mFakeExecutor.runAllReady();
+        verify(mTileLifecycle, never()).onStopListening();
+        verify(mTileLifecycle, never()).executeSetBindService(false);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index b62d59d..bcff88a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -15,6 +15,10 @@
  */
 package com.android.systemui.qs.external;
 
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
 
@@ -33,8 +37,10 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.service.quicksettings.IQSTileService;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -64,13 +70,23 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.List;
 
 import javax.inject.Provider;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 @RunWithLooper
 public class TileServicesTest extends SysuiTestCase {
+
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+    }
+
     private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2;
 
     private static final ComponentName TEST_COMPONENT =
@@ -106,6 +122,11 @@
     @Mock
     private CustomTileAddedRepository mCustomTileAddedRepository;
 
+    public TileServicesTest(FlagsParameterization flags) {
+        super();
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -194,6 +215,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
     public void testRequestListeningStatusCommand() throws RemoteException {
         ArgumentCaptor<CommandQueue.Callbacks> captor =
                 ArgumentCaptor.forClass(CommandQueue.Callbacks.class);
@@ -213,6 +235,26 @@
     }
 
     @Test
+    @EnableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
+    public void testRequestListeningStatusCommand_onStartListeningFromRequest() {
+        ArgumentCaptor<CommandQueue.Callbacks> captor =
+                ArgumentCaptor.forClass(CommandQueue.Callbacks.class);
+        verify(mCommandQueue).addCallback(captor.capture());
+
+        CustomTile mockTile = mock(CustomTile.class);
+        when(mockTile.getComponent()).thenReturn(TEST_COMPONENT);
+
+        TileServiceManager manager = mTileService.getTileWrapper(mockTile);
+        when(manager.isActiveTile()).thenReturn(true);
+        when(manager.getTileService()).thenReturn(mock(IQSTileService.class));
+
+        captor.getValue().requestTileServiceListeningState(TEST_COMPONENT);
+        mTestableLooper.processAllMessages();
+        verify(manager).setBindRequested(true);
+        verify(manager).onStartListeningFromRequest();
+    }
+
+    @Test
     public void testValidCustomTileStartsActivity() {
         CustomTile tile = mock(CustomTile.class);
         PendingIntent pi = mock(PendingIntent.class);
@@ -263,6 +305,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
     public void tileFreedForCorrectUser() throws RemoteException {
         verify(mCommandQueue).addCallback(mCallbacksArgumentCaptor.capture());
 
@@ -297,6 +340,42 @@
         verify(manager1.getTileService()).onStartListening();
     }
 
+    @Test
+    @EnableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
+    public void tileFreedForCorrectUser_onStartListeningFromRequest() throws RemoteException {
+        verify(mCommandQueue).addCallback(mCallbacksArgumentCaptor.capture());
+
+        ComponentName componentName = new ComponentName("pkg", "cls");
+        CustomTile tileUser0 = mock(CustomTile.class);
+        CustomTile tileUser1 = mock(CustomTile.class);
+
+        when(tileUser0.getComponent()).thenReturn(componentName);
+        when(tileUser1.getComponent()).thenReturn(componentName);
+        when(tileUser0.getUser()).thenReturn(0);
+        when(tileUser1.getUser()).thenReturn(1);
+
+        // Create a tile for user 0
+        TileServiceManager manager0 = mTileService.getTileWrapper(tileUser0);
+        when(manager0.isActiveTile()).thenReturn(true);
+        // Then create a tile for user 1
+        TileServiceManager manager1 = mTileService.getTileWrapper(tileUser1);
+        when(manager1.isActiveTile()).thenReturn(true);
+
+        // When the tile for user 0 gets freed
+        mTileService.freeService(tileUser0, manager0);
+        // and the user is 1
+        when(mUserTracker.getUserId()).thenReturn(1);
+
+        // a call to requestListeningState
+        mCallbacksArgumentCaptor.getValue().requestTileServiceListeningState(componentName);
+        mTestableLooper.processAllMessages();
+
+        // will call in the correct tile
+        verify(manager1).setBindRequested(true);
+        // and set it to listening
+        verify(manager1).onStartListeningFromRequest();
+    }
+
     private class TestTileServices extends TileServices {
         TestTileServices(QSHost host, Provider<Handler> handlerProvider,
                 BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
index db752dd..d15cfbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
 import com.android.systemui.qs.panels.data.repository.IconTilesRepository
 import com.android.systemui.qs.panels.data.repository.gridLayoutTypeRepository
 import com.android.systemui.qs.panels.data.repository.iconTilesRepository
@@ -48,9 +47,6 @@
 
     data object TestGridLayoutType : GridLayoutType
 
-    private val gridLayout: MutableStateFlow<GridLayoutType> =
-        MutableStateFlow(InfiniteGridLayoutType)
-
     private val iconOnlyTiles =
         MutableStateFlow(
             setOf(
@@ -74,17 +70,13 @@
                     Pair(InfiniteGridLayoutType, infiniteGridConsistencyInteractor),
                     Pair(TestGridLayoutType, noopGridConsistencyInteractor)
                 )
-            gridLayoutTypeRepository =
-                object : GridLayoutTypeRepository {
-                    override val layout: StateFlow<GridLayoutType> = gridLayout.asStateFlow()
-                }
         }
 
     private val underTest = with(kosmos) { gridConsistencyInteractor }
 
     @Before
     fun setUp() {
-        gridLayout.value = InfiniteGridLayoutType
+        with(kosmos) { gridLayoutTypeRepository.setLayout(InfiniteGridLayoutType) }
         underTest.start()
     }
 
@@ -94,7 +86,7 @@
         with(kosmos) {
             testScope.runTest {
                 // Using the no-op grid consistency interactor
-                gridLayout.value = TestGridLayoutType
+                gridLayoutTypeRepository.setLayout(TestGridLayoutType)
 
                 // Setting an invalid layout with holes
                 // [ Large A ] [ sa ]
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
index e2a3fac6..ad87315 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
@@ -22,7 +22,7 @@
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
+import com.android.internal.telephony.flags.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.classifier.FalsingManagerFake
@@ -33,10 +33,12 @@
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.settings.GlobalSettings
 import com.google.common.truth.Truth.assertThat
 import dagger.Lazy
+import kotlinx.coroutines.Job
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
@@ -44,11 +46,15 @@
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class AirplaneModeTileTest : SysuiTestCase() {
+
     @Mock
     private lateinit var mHost: QSHost
     @Mock
@@ -62,7 +68,9 @@
     @Mock
     private lateinit var mBroadcastDispatcher: BroadcastDispatcher
     @Mock
-    private lateinit var mConnectivityManager: Lazy<ConnectivityManager>
+    private lateinit var mLazyConnectivityManager: Lazy<ConnectivityManager>
+    @Mock
+    private lateinit var mConnectivityManager: ConnectivityManager
     @Mock
     private lateinit var mGlobalSettings: GlobalSettings
     @Mock
@@ -72,13 +80,15 @@
     private lateinit var mTestableLooper: TestableLooper
     private lateinit var mTile: AirplaneModeTile
 
+    @Mock
+    private lateinit var mClickJob: Job
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         mTestableLooper = TestableLooper.get(this)
         Mockito.`when`(mHost.context).thenReturn(mContext)
         Mockito.`when`(mHost.userContext).thenReturn(mContext)
-
+        Mockito.`when`(mLazyConnectivityManager.get()).thenReturn(mConnectivityManager)
         mTile = AirplaneModeTile(
             mHost,
             mUiEventLogger,
@@ -90,7 +100,7 @@
             mActivityStarter,
             mQsLogger,
             mBroadcastDispatcher,
-            mConnectivityManager,
+            mLazyConnectivityManager,
             mGlobalSettings,
             mUserTracker)
     }
@@ -120,4 +130,24 @@
         assertThat(state.icon)
             .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_airplane_icon_on))
     }
+
+    @Test
+    fun handleClick_noSatelliteFeature_directSetAirplaneMode() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+
+        mTile.handleClick(null)
+
+        verify(mConnectivityManager).setAirplaneMode(any())
+    }
+
+    @Test
+    fun handleClick_hasSatelliteFeatureButClickIsProcessing_doNothing() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        Mockito.`when`(mClickJob.isCompleted).thenReturn(false)
+        mTile.mClickJob = mClickJob
+
+        mTile.handleClick(null)
+
+        verify(mConnectivityManager, times(0)).setAirplaneMode(any())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index 830f08a..1ffbb7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -9,10 +9,11 @@
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
+import com.android.internal.telephony.flags.Flags
 import com.android.settingslib.Utils
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.plugins.ActivityStarter
@@ -23,13 +24,14 @@
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.BluetoothController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Job
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
@@ -37,6 +39,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 @RunWith(AndroidTestingRunner::class)
@@ -54,7 +57,7 @@
     @Mock private lateinit var uiEventLogger: QsEventLogger
     @Mock private lateinit var featureFlags: FeatureFlagsClassic
     @Mock private lateinit var bluetoothTileDialogViewModel: BluetoothTileDialogViewModel
-
+    @Mock private lateinit var clickJob: Job
     private lateinit var testableLooper: TestableLooper
     private lateinit var tile: FakeBluetoothTile
 
@@ -191,6 +194,41 @@
     }
 
     @Test
+    fun handleClick_hasSatelliteFeatureButNoQsTileDialogAndClickIsProcessing_doNothing() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        `when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
+                .thenReturn(false)
+        `when`(clickJob.isCompleted).thenReturn(false)
+        tile.mClickJob = clickJob
+
+        tile.handleClick(null)
+
+        verify(bluetoothController, times(0)).setBluetoothEnabled(any())
+    }
+
+    @Test
+    fun handleClick_noSatelliteFeatureAndNoQsTileDialog_directSetBtEnable() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        `when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
+                .thenReturn(false)
+
+        tile.handleClick(null)
+
+        verify(bluetoothController).setBluetoothEnabled(any())
+    }
+
+    @Test
+    fun handleClick_noSatelliteFeatureButHasQsTileDialog_showDialog() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        `when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
+                .thenReturn(true)
+
+        tile.handleClick(null)
+
+        verify(bluetoothTileDialogViewModel).showDialog(null)
+    }
+
+    @Test
     fun testMetadataListener_whenDisconnected_isUnregistered() {
         val state = QSTile.BooleanState()
         val cachedDevice = mock<CachedBluetoothDevice>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
index f88a5a0..b75b318 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
@@ -20,7 +20,7 @@
 import static junit.framework.Assert.assertNotSame;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -81,7 +81,7 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
-        when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+        when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
         when(mSystemUIDialogFactory.create()).thenReturn(mSystemUIDialog);
         when(mSystemUIDialog.getContext()).thenReturn(mContext);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index effae5f..74deae3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -72,7 +72,7 @@
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.atLeast
 import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.intThat
+import org.mockito.Mockito.longThat
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.times
@@ -162,7 +162,7 @@
 
         verify(overviewProxy)
             .onSystemUiStateChanged(
-                intThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_AWAKE }
+                longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_AWAKE }
             )
     }
 
@@ -172,7 +172,7 @@
 
         verify(overviewProxy)
             .onSystemUiStateChanged(
-                intThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_WAKING }
+                longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_WAKING }
             )
     }
 
@@ -182,7 +182,7 @@
 
         verify(overviewProxy)
             .onSystemUiStateChanged(
-                intThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_ASLEEP }
+                longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_ASLEEP }
             )
     }
 
@@ -194,7 +194,7 @@
 
         verify(overviewProxy)
             .onSystemUiStateChanged(
-                intThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_GOING_TO_SLEEP }
+                longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_GOING_TO_SLEEP }
             )
     }
 
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 6846c72..fcc6b4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -55,6 +55,7 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.spy
@@ -94,7 +95,7 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
         whenever(dprLazy.get()).thenReturn(devicePolicyResolver)
-        whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
+        whenever(sysuiState.setFlag(anyLong(), anyBoolean())).thenReturn(sysuiState)
         whenever(screenCaptureDisabledDialogDelegate.createSysUIDialog())
             .thenReturn(screenCaptureDisabledDialog)
         whenever(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
index 5e7d8fb..a10d81f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
@@ -21,7 +21,6 @@
 import android.os.Bundle
 import android.os.UserHandle
 import android.testing.AndroidTestingRunner
-import android.view.View
 import android.view.Window
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -49,7 +48,7 @@
 
     private val intentExecutor = mock<ActionIntentExecutor>()
     private val window = mock<Window>()
-    private val view = mock<View>()
+    private val viewProxy = mock<ScreenshotShelfViewProxy>()
     private val onDismiss = mock<(() -> Unit)>()
     private val pendingIntent = mock<PendingIntent>()
 
@@ -70,16 +69,16 @@
     }
 
     @Test
-    fun sendPendingIntent_dismisses() = runTest {
+    fun sendPendingIntent_requestsDismissal() = runTest {
         actionExecutor = createActionExecutor()
 
         actionExecutor.sendPendingIntent(pendingIntent)
 
         verify(pendingIntent).send(any(Bundle::class.java))
-        verify(onDismiss).invoke()
+        verify(viewProxy).requestDismissal(null)
     }
 
     private fun createActionExecutor(): ActionExecutor {
-        return ActionExecutor(intentExecutor, testScope, window, view, onDismiss)
+        return ActionExecutor(intentExecutor, testScope, window, viewProxy, onDismiss)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
index 5e53fe1..5cd3f66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
@@ -23,17 +23,18 @@
 import android.testing.TestableContext
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.screenshot.proxy.SystemUiProxy
 import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.shared.system.ActivityManagerWrapper
 import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestCoroutineScheduler
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
 
 @RunWith(AndroidTestingRunner::class)
 class ActionIntentExecutorTest : SysuiTestCase() {
@@ -44,8 +45,9 @@
     private val testableContext = TestableContext(mContext)
 
     private val activityManagerWrapper = mock<ActivityManagerWrapper>()
+    private val systemUiProxy = mock<SystemUiProxy>()
+
     private val displayTracker = mock<DisplayTracker>()
-    private val keyguardController = mock<ScreenshotKeyguardController>()
 
     private val actionIntentExecutor =
         ActionIntentExecutor(
@@ -53,12 +55,12 @@
             activityManagerWrapper,
             testScope,
             mainDispatcher,
+            systemUiProxy,
             displayTracker,
-            keyguardController,
         )
 
     @Test
-    @EnableFlags(Flags.FLAG_SCREENSHOT_ACTION_DISMISS_SYSTEM_WINDOWS)
+    @EnableFlags(Flags.FLAG_FIX_SCREENSHOT_ACTION_DISMISS_SYSTEM_WINDOWS)
     fun launchIntent_callsCloseSystemWindows() =
         testScope.runTest {
             val intent = Intent(Intent.ACTION_EDIT).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
index 853e50a..896c3bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
@@ -37,6 +37,7 @@
 import org.junit.Before
 import org.junit.runner.RunWith
 import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.kotlin.never
 import org.mockito.kotlin.verify
 
 @RunWith(AndroidTestingRunner::class)
@@ -111,6 +112,47 @@
         assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_CHOOSER)
     }
 
+    @Test
+    fun scrollChipClicked_callsOnClick() = runTest {
+        actionsProvider = createActionsProvider()
+
+        val onScrollClick = mock<Runnable>()
+        val numActions = viewModel.actions.value.size
+        actionsProvider.onScrollChipReady(onScrollClick)
+        viewModel.actions.value[numActions].onClicked!!.invoke()
+
+        verify(onScrollClick).run()
+    }
+
+    @Test
+    fun scrollChipClicked_afterInvalidate_doesNothing() = runTest {
+        actionsProvider = createActionsProvider()
+
+        val onScrollClick = mock<Runnable>()
+        val numActions = viewModel.actions.value.size
+        actionsProvider.onScrollChipReady(onScrollClick)
+        actionsProvider.onScrollChipInvalidated()
+        viewModel.actions.value[numActions].onClicked!!.invoke()
+
+        verify(onScrollClick, never()).run()
+    }
+
+    @Test
+    fun scrollChipClicked_afterUpdate_runsNewAction() = runTest {
+        actionsProvider = createActionsProvider()
+
+        val onScrollClick = mock<Runnable>()
+        val onScrollClick2 = mock<Runnable>()
+        val numActions = viewModel.actions.value.size
+        actionsProvider.onScrollChipReady(onScrollClick)
+        actionsProvider.onScrollChipInvalidated()
+        actionsProvider.onScrollChipReady(onScrollClick2)
+        viewModel.actions.value[numActions].onClicked!!.invoke()
+
+        verify(onScrollClick2).run()
+        verify(onScrollClick, never()).run()
+    }
+
     private fun createActionsProvider(): ScreenshotActionsProvider {
         return DefaultScreenshotActionsProvider(
             context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 537049c..49a467e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -40,6 +40,7 @@
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.ui.compose.CommunalContent
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.util.CommunalColors
 import com.android.systemui.coroutines.collectLastValue
@@ -50,7 +51,6 @@
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.sceneDataSourceDelegator
 import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
@@ -83,9 +83,9 @@
 
     @Mock private lateinit var communalViewModel: CommunalViewModel
     @Mock private lateinit var powerManager: PowerManager
-    @Mock private lateinit var dialogFactory: SystemUIDialogFactory
     @Mock private lateinit var touchMonitor: TouchMonitor
     @Mock private lateinit var communalColors: CommunalColors
+    @Mock private lateinit var communalContent: CommunalContent
     private lateinit var ambientTouchComponentFactory: AmbientTouchComponent.Factory
 
     private lateinit var parentView: FrameLayout
@@ -117,12 +117,12 @@
                 GlanceableHubContainerController(
                     communalInteractor,
                     communalViewModel,
-                    dialogFactory,
                     keyguardInteractor,
                     shadeInteractor,
                     powerManager,
                     communalColors,
                     ambientTouchComponentFactory,
+                    communalContent,
                     kosmos.sceneDataSourceDelegator,
                 )
         }
@@ -159,12 +159,12 @@
                     GlanceableHubContainerController(
                         communalInteractor,
                         communalViewModel,
-                        dialogFactory,
                         keyguardInteractor,
                         shadeInteractor,
                         powerManager,
                         communalColors,
                         ambientTouchComponentFactory,
+                        communalContent,
                         kosmos.sceneDataSourceDelegator,
                     )
 
@@ -303,12 +303,12 @@
                 GlanceableHubContainerController(
                     communalInteractor,
                     communalViewModel,
-                    dialogFactory,
                     keyguardInteractor,
                     shadeInteractor,
                     powerManager,
                     communalColors,
                     ambientTouchComponentFactory,
+                    communalContent,
                     kosmos.sceneDataSourceDelegator,
                 )
 
@@ -322,12 +322,12 @@
                 GlanceableHubContainerController(
                     communalInteractor,
                     communalViewModel,
-                    dialogFactory,
                     keyguardInteractor,
                     shadeInteractor,
                     powerManager,
                     communalColors,
                     ambientTouchComponentFactory,
+                    communalContent,
                     kosmos.sceneDataSourceDelegator,
                 )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 5b47c94..4d32cc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -40,6 +40,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.animation.Animator;
 import android.annotation.IdRes;
 import android.content.ContentResolver;
 import android.content.res.Configuration;
@@ -207,12 +208,15 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.mockito.stubbing.Answer;
 
+import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
 
@@ -387,9 +391,11 @@
 
     protected FragmentHostManager.FragmentListener mFragmentListener;
 
+    @Rule(order = 200)
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
     @Before
     public void setup() {
-        MockitoAnnotations.initMocks(this);
         mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
         mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
 
@@ -547,6 +553,8 @@
         }).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class));
 
         // Dreaming->Lockscreen
+        when(mKeyguardTransitionInteractor.transition(any()))
+                .thenReturn(emptyFlow());
         when(mKeyguardTransitionInteractor.transition(any(), any()))
                 .thenReturn(emptyFlow());
         when(mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha())
@@ -655,7 +663,7 @@
         when(mView.getParent()).thenReturn(mViewParent);
         when(mQs.getHeader()).thenReturn(mQsHeader);
         when(mDownMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN);
-        when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+        when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
 
         mMainHandler = new Handler(Looper.getMainLooper());
 
@@ -759,6 +767,9 @@
                     @Override
                     public void onOpenStarted() {}
                 });
+        // Create a set to which the class will add all animators used, so that we can
+        // verify that they are all stopped.
+        mNotificationPanelViewController.mTestSetOfAnimatorsUsed = new HashSet<>();
         ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
                 ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
         verify(mView, atLeast(1)).addOnAttachStateChangeListener(
@@ -820,13 +831,20 @@
 
     @After
     public void tearDown() {
+        List<Animator> leakedAnimators = null;
         if (mNotificationPanelViewController != null) {
             mNotificationPanelViewController.mBottomAreaShadeAlphaAnimator.cancel();
             mNotificationPanelViewController.cancelHeightAnimator();
+            leakedAnimators = mNotificationPanelViewController.mTestSetOfAnimatorsUsed.stream()
+                    .filter(Animator::isRunning).toList();
+            mNotificationPanelViewController.mTestSetOfAnimatorsUsed.forEach(Animator::cancel);
         }
         if (mMainHandler != null) {
             mMainHandler.removeCallbacksAndMessages(null);
         }
+        if (leakedAnimators != null) {
+            assertThat(leakedAnimators).isEmpty();
+        }
     }
 
     protected void setBottomPadding(int stackBottom, int lockIconPadding, int indicationPadding,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index e1cdda4..6536405 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -705,6 +705,7 @@
     }
 
     @Test
+    @Ignore("b/341163515 - fails to clean up animators correctly")
     public void testSwipeWhileLocked_notifiesKeyguardState() {
         mStatusBarStateController.setState(KEYGUARD);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 45d0102..4a867a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -46,6 +46,7 @@
 import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -173,7 +174,7 @@
                 .thenReturn(keyguardBouncerComponent)
         whenever(keyguardBouncerComponent.securityContainerController)
                 .thenReturn(keyguardSecurityContainerController)
-        whenever(keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING))
+        whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING)))
                 .thenReturn(emptyFlow<TransitionStep>())
 
         featureFlagsClassic = FakeFeatureFlagsClassic()
@@ -518,46 +519,6 @@
     }
 
     @Test
-    fun handleExternalTouch_intercepted_sendsOnTouch() {
-        // Accept dispatch and also intercept.
-        whenever(view.dispatchTouchEvent(any())).thenReturn(true)
-        whenever(view.onInterceptTouchEvent(any())).thenReturn(true)
-
-        underTest.handleExternalTouch(DOWN_EVENT)
-        underTest.handleExternalTouch(MOVE_EVENT)
-
-        // Once intercepted, both events are sent to the view.
-        verify(view).onTouchEvent(DOWN_EVENT)
-        verify(view).onTouchEvent(MOVE_EVENT)
-    }
-
-    @Test
-    fun handleExternalTouch_notDispatched_interceptNotCalled() {
-        // Don't accept dispatch
-        whenever(view.dispatchTouchEvent(any())).thenReturn(false)
-
-        underTest.handleExternalTouch(DOWN_EVENT)
-
-        // Interception is not offered.
-        verify(view, never()).onInterceptTouchEvent(any())
-    }
-
-    @Test
-    fun handleExternalTouch_notIntercepted_onTouchNotSent() {
-        // Accept dispatch, but don't dispatch
-        whenever(view.dispatchTouchEvent(any())).thenReturn(true)
-        whenever(view.onInterceptTouchEvent(any())).thenReturn(false)
-
-        underTest.handleExternalTouch(DOWN_EVENT)
-        underTest.handleExternalTouch(MOVE_EVENT)
-
-        // Interception offered for both events, but onTouchEvent is never called.
-        verify(view).onInterceptTouchEvent(DOWN_EVENT)
-        verify(view).onInterceptTouchEvent(MOVE_EVENT)
-        verify(view, never()).onTouchEvent(any())
-    }
-
-    @Test
     fun testGetKeyguardMessageArea() =
         testScope.runTest {
             underTest.keyguardMessageArea
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index f380b6c..e83a46b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.res.R
@@ -151,7 +152,7 @@
         whenever(statusBarStateController.isDozing).thenReturn(false)
         mDependency.injectTestDependency(ShadeController::class.java, shadeController)
         whenever(dockManager.isDocked).thenReturn(false)
-        whenever(keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING))
+        whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING)))
             .thenReturn(emptyFlow())
 
         val featureFlags = FakeFeatureFlags()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 81d0e06..2c2fcbe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.shade
 
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
@@ -26,8 +28,8 @@
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
+import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.fragments.FragmentHostManager
 import com.android.systemui.fragments.FragmentService
@@ -164,10 +166,10 @@
     }
 
     @Test
+    @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSetBasedOnResource() {
         val headerResourceHeight = 20
         val headerHelperHeight = 30
-        mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
         whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
             .thenReturn(headerHelperHeight)
         overrideResource(R.bool.config_use_large_screen_shade_header, true)
@@ -187,10 +189,10 @@
     }
 
     @Test
+    @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSetBasedOnHelper() {
         val headerResourceHeight = 20
         val headerHelperHeight = 30
-        mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
         whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
             .thenReturn(headerHelperHeight)
         overrideResource(R.bool.config_use_large_screen_shade_header, true)
@@ -400,8 +402,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun testSplitShadeLayout_isAlignedToGuideline() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         enableSplitShade()
         underTest.updateResources()
         assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd).isEqualTo(R.id.qs_edge_guideline)
@@ -410,8 +412,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun testSinglePaneLayout_childrenHaveEqualMargins() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         disableSplitShade()
         underTest.updateResources()
         val qsStartMargin = getConstraintSetLayout(R.id.qs_frame).startMargin
@@ -427,8 +429,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun testSplitShadeLayout_childrenHaveInsideMarginsOfZero() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         enableSplitShade()
         underTest.updateResources()
         assertThat(getConstraintSetLayout(R.id.qs_frame).endMargin).isEqualTo(0)
@@ -445,9 +447,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderHeightResource() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-        mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
         setLargeScreen()
         val largeScreenHeaderResourceHeight = 100
         val largeScreenHeaderHelperHeight = 200
@@ -468,9 +469,9 @@
     }
 
     @Test
+    @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+    @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHeightHelper() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-        mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
         setLargeScreen()
         val largeScreenHeaderResourceHeight = 100
         val largeScreenHeaderHelperHeight = 200
@@ -491,8 +492,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun testSmallScreenLayout_qsAndNotifsTopMarginIsZero() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         setSmallScreen()
         underTest.updateResources()
         assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin).isEqualTo(0)
@@ -512,8 +513,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun testSinglePaneShadeLayout_isAlignedToParent() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         disableSplitShade()
         underTest.updateResources()
         assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index 4ae751b..f21def3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.shade
 
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
@@ -27,6 +29,7 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
+import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.fragments.FragmentHostManager
 import com.android.systemui.fragments.FragmentService
@@ -67,6 +70,7 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
+@EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
 class NotificationsQSContainerControllerTest : SysuiTestCase() {
 
     private val view = mock<NotificationsQuickSettingsContainer>()
@@ -99,7 +103,6 @@
         MockitoAnnotations.initMocks(this)
         fakeSystemClock = FakeSystemClock()
         delayableExecutor = FakeExecutor(fakeSystemClock)
-        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         mContext.ensureTestableResources()
         whenever(view.context).thenReturn(mContext)
         whenever(view.resources).thenReturn(mContext.resources)
@@ -161,8 +164,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSet_basedOnResource() {
-        mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
         val helperHeight = 30
         val resourceHeight = 20
         whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(helperHeight)
@@ -182,8 +185,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSet_basedOnHelper() {
-        mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
         val helperHeight = 30
         val resourceHeight = 20
         whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(helperHeight)
@@ -424,8 +427,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderResourceHeight() {
-        mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
         setLargeScreen()
         val largeScreenHeaderHelperHeight = 200
         val largeScreenHeaderResourceHeight = 100
@@ -444,8 +447,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHelperHeight() {
-        mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
         setLargeScreen()
         val largeScreenHeaderHelperHeight = 200
         val largeScreenHeaderResourceHeight = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 04fa590..845744a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -42,13 +42,10 @@
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlagsClassic;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
-import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
@@ -59,9 +56,7 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.qs.QSFragmentLegacy;
 import com.android.systemui.res.R;
-import com.android.systemui.scene.data.repository.SceneContainerRepository;
 import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.shared.logger.SceneLogger;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
@@ -176,12 +171,7 @@
     protected Handler mMainHandler;
     protected LockscreenShadeTransitionController.Callback mLockscreenShadeTransitionCallback;
 
-    protected final ShadeExpansionStateManager mShadeExpansionStateManager =
-            new ShadeExpansionStateManager();
-
     protected FragmentHostManager.FragmentListener mFragmentListener;
-    private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
-    private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
 
     @Before
     public void setup() {
@@ -190,19 +180,11 @@
         mStatusBarStateController = mKosmos.getStatusBarStateController();
 
         mKosmos.getFakeDeviceProvisioningRepository().setDeviceProvisioned(true);
-        FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
         FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
 
         PowerInteractor powerInteractor = mKosmos.getPowerInteractor();
 
-        SceneInteractor sceneInteractor = new SceneInteractor(
-                mTestScope.getBackgroundScope(),
-                new SceneContainerRepository(
-                        mTestScope.getBackgroundScope(),
-                        mKosmos.getFakeSceneContainerConfig(),
-                        mKosmos.getSceneDataSource()),
-                mock(SceneLogger.class),
-                mKosmos.getDeviceUnlockedInteractor());
+        SceneInteractor sceneInteractor = mKosmos.getSceneInteractor();
 
         KeyguardTransitionInteractor keyguardTransitionInteractor =
                 mKosmos.getKeyguardTransitionInteractor();
@@ -220,10 +202,6 @@
                 () -> mKosmos.getSharedNotificationContainerInteractor(),
                 mTestScope);
 
-        mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
-        mFromPrimaryBouncerTransitionInteractor =
-                mKosmos.getFromPrimaryBouncerTransitionInteractor();
-
         ResourcesSplitShadeStateController splitShadeStateController =
                 new ResourcesSplitShadeStateController();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
index 2c453a7..dfd7a71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelChildren
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -36,6 +37,8 @@
             runCurrent()
 
             assertThat(mQsController.isExpansionEnabled).isFalse()
+
+            coroutineContext.cancelChildren()
         }
 
     @Test
@@ -45,6 +48,8 @@
             runCurrent()
 
             assertThat(mQsController.isExpansionEnabled).isTrue()
+
+            coroutineContext.cancelChildren()
         }
 
     @Test
@@ -58,6 +63,8 @@
             runCurrent()
 
             assertThat(mQsController.isExpansionEnabled).isFalse()
+
+            coroutineContext.cancelChildren()
         }
 
     @Test
@@ -71,6 +78,8 @@
             runCurrent()
 
             assertThat(mQsController.isExpansionEnabled).isFalse()
+
+            coroutineContext.cancelChildren()
         }
 
     @Test
@@ -81,5 +90,7 @@
             runCurrent()
 
             assertThat(mQsController.isExpansionEnabled).isTrue()
+
+            coroutineContext.cancelChildren()
         }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
similarity index 86%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
index f0a457e..7d2b463 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone.ongoingcall
+package com.android.systemui.statusbar.chips.ui.view
 
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.LayoutInflater
 import android.view.View
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -31,16 +31,17 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
-class OngoingCallBackgroundContainerTest : SysuiTestCase() {
+class ChipBackgroundContainerTest : SysuiTestCase() {
 
-    private lateinit var underTest: OngoingCallBackgroundContainer
+    private lateinit var underTest: ChipBackgroundContainer
 
     @Before
     fun setUp() {
         allowTestableLooperAsMainThread()
         TestableLooper.get(this).runWithLooper {
-            val chipView = LayoutInflater.from(context).inflate(R.layout.ongoing_call_chip, null)
-            underTest = chipView.requireViewById(R.id.ongoing_call_chip_background)
+            val chipView =
+                LayoutInflater.from(context).inflate(R.layout.ongoing_activity_chip, null)
+            underTest = chipView.requireViewById(R.id.ongoing_activity_chip_background)
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
similarity index 89%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
index 7e25aa3..b8d4e47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone.ongoingcall
+package com.android.systemui.statusbar.chips.ui.view
 
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.LayoutInflater
 import android.view.View
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -38,17 +38,18 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
-class OngoingCallChronometerTest : SysuiTestCase() {
+class ChipChronometerTest : SysuiTestCase() {
 
-    private lateinit var textView: OngoingCallChronometer
+    private lateinit var textView: ChipChronometer
     private lateinit var doesNotFitText: String
 
     @Before
     fun setUp() {
         allowTestableLooperAsMainThread()
         TestableLooper.get(this).runWithLooper {
-            val chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null)
-            textView = chipView.findViewById(R.id.ongoing_call_chip_time)!!
+            val chipView =
+                LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null)
+            textView = chipView.findViewById(R.id.ongoing_activity_chip_time)!!
             measureTextView()
             calculateDoesNotFixText()
         }
@@ -159,8 +160,8 @@
 
     private fun measureTextView() {
         textView.measure(
-                View.MeasureSpec.makeMeasureSpec(TEXT_VIEW_MAX_WIDTH, View.MeasureSpec.AT_MOST),
-                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+            View.MeasureSpec.makeMeasureSpec(TEXT_VIEW_MAX_WIDTH, View.MeasureSpec.AT_MOST),
+            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
         )
     }
 
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 745d20d..1eed420 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
@@ -1,5 +1,6 @@
 package com.android.systemui.statusbar.notification.stack
 
+import android.platform.test.annotations.DisableFlags
 import android.service.notification.StatusBarNotification
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
@@ -69,8 +70,8 @@
     }
 
     @Test
+    @DisableFlags(NotificationIconContainerRefactor.FLAG_NAME)
     fun testShadeWidth_BasedOnFractionToShade() {
-        mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
         setFractionToShade(0f)
         setOnLockscreen(true)
 
@@ -85,8 +86,8 @@
     }
 
     @Test
+    @DisableFlags(NotificationIconContainerRefactor.FLAG_NAME)
     fun testShelfIsLong_WhenNotOnLockscreen() {
-        mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
         setFractionToShade(0f)
         setOnLockscreen(false)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index c088609..fe6a88d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -51,6 +51,7 @@
 import com.android.systemui.shade.ShadeHeaderController;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -80,6 +81,7 @@
     @Mock private QuickSettingsController mQuickSettingsController;
     @Mock private ShadeViewController mShadeViewController;
     @Mock private PanelExpansionInteractor mPanelExpansionInteractor;
+    @Mock private Lazy<ShadeInteractor> mShadeInteractorLazy;
     @Mock private ShadeHeaderController mShadeHeaderController;
     @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
     private final MetricsLogger mMetricsLogger = new FakeMetricsLogger();
@@ -115,6 +117,7 @@
                 mShadeController,
                 mCommandQueue,
                 mPanelExpansionInteractor,
+                mShadeInteractorLazy,
                 mShadeHeaderController,
                 mRemoteInputQuickSettingsDisabler,
                 mMetricsLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index b9312d3..4488799 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -21,6 +21,7 @@
 import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
 import static android.provider.Settings.Global.HEADS_UP_ON;
 
+import static com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR;
 import static com.android.systemui.Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE;
 import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION;
 import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
@@ -70,6 +71,8 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.service.dreams.IDreamManager;
 import android.support.test.metricshelper.MetricsAsserts;
 import android.testing.AndroidTestingRunner;
@@ -105,8 +108,6 @@
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.communal.data.repository.CommunalRepository;
-import com.android.systemui.communal.domain.interactor.CommunalInteractor;
 import com.android.systemui.communal.shared.model.CommunalScenes;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
@@ -224,6 +225,7 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
+@EnableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
 public class CentralSurfacesImplTest extends SysuiTestCase {
 
     private static final int FOLD_STATE_FOLDED = 0;
@@ -238,8 +240,6 @@
 
 
     private final TestScope mTestScope = mKosmos.getTestScope();
-    private final CommunalInteractor mCommunalInteractor = mKosmos.getCommunalInteractor();
-    private final CommunalRepository mCommunalRepository = mKosmos.getCommunalRepository();
     @Mock private NotificationsController mNotificationsController;
     @Mock private LightBarController mLightBarController;
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -362,13 +362,9 @@
 
         // Set default value to avoid IllegalStateException.
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, false);
-        mSetFlagsRule.enableFlags(FLAG_LIGHT_REVEAL_MIGRATION);
         // Turn AOD on and toggle feature flag for jank fixes
         mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
         when(mDozeParameters.getAlwaysOn()).thenReturn(true);
-        if (!SceneContainerFlag.isEnabled()) {
-            mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
-        }
 
         IThermalService thermalService = mock(IThermalService.class);
         mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
@@ -528,7 +524,7 @@
                 mScreenLifecycle,
                 mWakefulnessLifecycle,
                 mPowerInteractor,
-                mCommunalInteractor,
+                mKosmos.getCommunalInteractor(),
                 mStatusBarStateController,
                 Optional.of(mBubbles),
                 () -> mNoteTaskController,
@@ -837,6 +833,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     public void testSetDozingNotUnlocking_transitionToAuthScrimmed_cancelKeyguardFadingAway() {
         when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
         when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
@@ -848,7 +845,8 @@
     }
 
     @Test
-    public void testOccludingQSNotExpanded_transitionToAuthScrimmed() {
+    @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+    public void testOccludingQSNotExpanded_flagOff_transitionToAuthScrimmed() {
         when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
 
         // GIVEN device occluded and panel is NOT expanded
@@ -862,6 +860,39 @@
     }
 
     @Test
+    @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+    public void testNotOccluding_QSNotExpanded_flagOn_doesNotTransitionScrimState() {
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+        // GIVEN device occluded and panel is NOT expanded
+        mCentralSurfaces.setBarStateForTest(SHADE);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
+        when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(false);
+
+        mCentralSurfaces.updateScrimController();
+
+        // Tests the safeguard to reset the scrimstate
+        verify(mScrimController, never()).transitionTo(any());
+    }
+
+    @Test
+    @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+    public void testNotOccluding_QSExpanded_flagOn_doesTransitionScrimStateToKeyguard() {
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+        // GIVEN device occluded and panel is NOT expanded
+        mCentralSurfaces.setBarStateForTest(SHADE);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
+        when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true);
+
+        mCentralSurfaces.updateScrimController();
+
+        // Tests the safeguard to reset the scrimstate
+        verify(mScrimController, never()).transitionTo(eq(ScrimState.KEYGUARD));
+    }
+
+    @Test
+    @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     public void testOccludingQSExpanded_transitionToAuthScrimmedShade() {
         when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
 
@@ -878,16 +909,18 @@
     @Test
     public void testEnteringGlanceableHub_updatesScrim() {
         // Transition to the glanceable hub.
-        mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle(
-                CommunalScenes.Communal)));
+        mKosmos.getCommunalRepository()
+                .setTransitionState(
+                        flowOf(new ObservableTransitionState.Idle(CommunalScenes.Communal)));
         mTestScope.getTestScheduler().runCurrent();
 
         // ScrimState also transitions.
         verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB);
 
         // Transition away from the glanceable hub.
-        mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle(
-                CommunalScenes.Blank)));
+        mKosmos.getCommunalRepository()
+                .setTransitionState(
+                        flowOf(new ObservableTransitionState.Idle(CommunalScenes.Blank)));
         mTestScope.getTestScheduler().runCurrent();
 
         // ScrimState goes back to UNLOCKED.
@@ -901,16 +934,18 @@
         when(mKeyguardUpdateMonitor.isDreaming()).thenReturn(true);
 
         // Transition to the glanceable hub.
-        mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle(
-                CommunalScenes.Communal)));
+        mKosmos.getCommunalRepository()
+                .setTransitionState(
+                        flowOf(new ObservableTransitionState.Idle(CommunalScenes.Communal)));
         mTestScope.getTestScheduler().runCurrent();
 
         // ScrimState also transitions.
         verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
 
         // Transition away from the glanceable hub.
-        mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle(
-                CommunalScenes.Blank)));
+        mKosmos.getCommunalRepository()
+                .setTransitionState(
+                        flowOf(new ObservableTransitionState.Idle(CommunalScenes.Blank)));
         mTestScope.getTestScheduler().runCurrent();
 
         // ScrimState goes back to UNLOCKED.
@@ -1105,18 +1140,16 @@
     }
 
     @Test
+    @EnableFlags(com.android.systemui.Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX)
     public void updateResources_flagEnabled_doesNotUpdateStatusBarWindowHeight() {
-        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX);
-
         mCentralSurfaces.updateResources();
 
         verify(mStatusBarWindowController, never()).refreshStatusBarHeight();
     }
 
     @Test
+    @DisableFlags(com.android.systemui.Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX)
     public void updateResources_flagDisabled_updatesStatusBarWindowHeight() {
-        mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX);
-
         mCentralSurfaces.updateResources();
 
         verify(mStatusBarWindowController).refreshStatusBarHeight();
@@ -1151,10 +1184,10 @@
     }
 
     @Test
+    @EnableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void dismissKeyboardShortcuts_largeScreen_bothFlagsEnabled_doesNotDismissAny() {
         switchToLargeScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
-        mSetFlagsRule.enableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         dismissKeyboardShortcuts();
@@ -1163,10 +1196,10 @@
     }
 
     @Test
+    @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void dismissKeyboardShortcuts_largeScreen_newFlagsDisabled_dismissesTabletVersion() {
         switchToLargeScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
-        mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         dismissKeyboardShortcuts();
@@ -1175,10 +1208,10 @@
     }
 
     @Test
+    @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void dismissKeyboardShortcuts_largeScreen_bothFlagsDisabled_dismissesPhoneVersion() {
         switchToLargeScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, false);
-        mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         dismissKeyboardShortcuts();
@@ -1188,10 +1221,10 @@
     }
 
     @Test
+    @EnableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void dismissKeyboardShortcuts_smallScreen_bothFlagsEnabled_doesNotDismissAny() {
         switchToSmallScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
-        mSetFlagsRule.enableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         dismissKeyboardShortcuts();
@@ -1200,10 +1233,10 @@
     }
 
     @Test
+    @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void dismissKeyboardShortcuts_smallScreen_newFlagsDisabled_dismissesPhoneVersion() {
         switchToSmallScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
-        mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         dismissKeyboardShortcuts();
@@ -1213,10 +1246,10 @@
     }
 
     @Test
+    @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void dismissKeyboardShortcuts_smallScreen_bothFlagsDisabled_dismissesPhoneVersion() {
         switchToSmallScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, false);
-        mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         dismissKeyboardShortcuts();
@@ -1226,10 +1259,10 @@
     }
 
     @Test
+    @EnableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void toggleKeyboardShortcuts_largeScreen_bothFlagsEnabled_doesNotTogglesAny() {
         switchToLargeScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
-        mSetFlagsRule.enableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         int deviceId = 321;
@@ -1239,10 +1272,10 @@
     }
 
     @Test
+    @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void toggleKeyboardShortcuts_largeScreen_newFlagsDisabled_togglesTabletVersion() {
         switchToLargeScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
-        mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         int deviceId = 654;
@@ -1253,10 +1286,10 @@
     }
 
     @Test
+    @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void toggleKeyboardShortcuts_largeScreen_bothFlagsDisabled_togglesPhoneVersion() {
         switchToLargeScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, false);
-        mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         int deviceId = 987;
@@ -1267,10 +1300,10 @@
     }
 
     @Test
+    @EnableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void toggleKeyboardShortcuts_smallScreen_bothFlagsEnabled_doesNotToggleAny() {
         switchToSmallScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
-        mSetFlagsRule.enableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         int deviceId = 789;
@@ -1280,10 +1313,10 @@
     }
 
     @Test
+    @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void toggleKeyboardShortcuts_smallScreen_newFlagsDisabled_togglesPhoneVersion() {
         switchToSmallScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
-        mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         int deviceId = 456;
@@ -1294,10 +1327,10 @@
     }
 
     @Test
+    @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
     public void toggleKeyboardShortcuts_smallScreen_bothFlagsDisabled_togglesPhoneVersion() {
         switchToSmallScreen();
         mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, false);
-        mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
         createCentralSurfaces();
 
         int deviceId = 123;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index fd295b5..e834693 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -26,6 +26,8 @@
 import static org.mockito.Mockito.when;
 
 import android.content.res.Resources;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.filters.SmallTest;
@@ -297,8 +299,8 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOff_usesResource() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX);
         int keyguardSplitShadeTopMargin = 100;
         int largeScreenHeaderHeightResource = 70;
         when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
@@ -316,8 +318,8 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOn_usesHelper() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX);
         int keyguardSplitShadeTopMargin = 100;
         int largeScreenHeaderHeightHelper = 50;
         int largeScreenHeaderHeightResource = 70;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index dfee737..5b2526e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -71,7 +71,6 @@
 import com.android.systemui.shade.ShadeViewStateProvider;
 import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.data.repository.FakeKeyguardStatusBarRepository;
 import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor;
@@ -144,8 +143,6 @@
     @Mock private SecureSettings mSecureSettings;
     @Mock private CommandQueue mCommandQueue;
     @Mock private KeyguardLogger mLogger;
-
-    @Mock private NotificationMediaManager mNotificationMediaManager;
     @Mock private StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory;
 
     private TestShadeViewStateProvider mShadeViewStateProvider;
@@ -225,7 +222,6 @@
                 mFakeExecutor,
                 mBackgroundExecutor,
                 mLogger,
-                mNotificationMediaManager,
                 mStatusOverlayHoverListenerFactory
         );
     }
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 66211c9..fdf77ae 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
@@ -426,7 +426,7 @@
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
     }
 
     @Test
@@ -438,7 +438,7 @@
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.VISIBLE,
-                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
         assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
@@ -452,7 +452,7 @@
                 StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
         assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
     }
 
     @Test
@@ -465,7 +465,7 @@
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
     }
 
     @Test
@@ -477,21 +477,21 @@
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.VISIBLE,
-                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
 
         // Ongoing call ended
         when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
 
         // Ongoing call started
         when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.VISIBLE,
-                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 05464f3..4d6798b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -106,7 +106,7 @@
     fun setUp() {
         allowTestableLooperAsMainThread()
         TestableLooper.get(this).runWithLooper {
-            chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null)
+            chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null)
         }
 
         MockitoAnnotations.initMocks(this)
@@ -206,7 +206,7 @@
                 View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
         )
 
-        assertThat(chipView.findViewById<View>(R.id.ongoing_call_chip_time)?.measuredWidth)
+        assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
                 .isEqualTo(0)
     }
 
@@ -222,7 +222,7 @@
                 View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
         )
 
-        assertThat(chipView.findViewById<View>(R.id.ongoing_call_chip_time)?.measuredWidth)
+        assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
                 .isGreaterThan(0)
     }
 
@@ -237,7 +237,7 @@
                 View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
         )
 
-        assertThat(chipView.findViewById<View>(R.id.ongoing_call_chip_time)?.measuredWidth)
+        assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
                 .isGreaterThan(0)
     }
 
@@ -472,7 +472,10 @@
 
         lateinit var newChipView: View
         TestableLooper.get(this).runWithLooper {
-            newChipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null)
+            newChipView = LayoutInflater.from(mContext).inflate(
+                    R.layout.ongoing_activity_chip,
+                    null
+            )
         }
 
         // Change the chip view associated with the controller.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 36df61d..96e599f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
 
+import android.annotation.SuppressLint
 import android.content.Intent
 import android.net.ConnectivityManager
 import android.net.Network
@@ -27,8 +28,10 @@
 import android.net.vcn.VcnTransportInfo
 import android.net.wifi.WifiInfo
 import android.net.wifi.WifiManager
+import android.os.Bundle
 import android.os.ParcelUuid
 import android.telephony.CarrierConfigManager
+import android.telephony.ServiceState
 import android.telephony.SubscriptionInfo
 import android.telephony.SubscriptionManager
 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
@@ -53,6 +56,7 @@
 import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
@@ -595,6 +599,51 @@
             assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
         }
 
+    @SuppressLint("UnspecifiedRegisterReceiverFlag")
+    @Test
+    fun testDeviceServiceStateFromBroadcast_eagerlyWatchesBroadcast() =
+        testScope.runTest {
+            // Value starts out empty (null)
+            assertThat(underTest.deviceServiceState.value).isNull()
+
+            // WHEN an appropriate intent gets sent out
+            val intent = serviceStateIntent(subId = -1, emergencyOnly = false)
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                intent,
+            )
+            runCurrent()
+
+            // THEN the repo's state is updated
+            val expected = ServiceStateModel(isEmergencyOnly = false)
+            assertThat(underTest.deviceServiceState.value).isEqualTo(expected)
+        }
+
+    @Test
+    fun testDeviceServiceStateFromBroadcast_followsSubIdNegativeOne() =
+        testScope.runTest {
+            // device based state tracks -1
+            val intent = serviceStateIntent(subId = -1, emergencyOnly = false)
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                intent,
+            )
+            runCurrent()
+
+            val deviceBasedState = ServiceStateModel(isEmergencyOnly = false)
+            assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState)
+
+            // ... and ignores any other subId
+            val intent2 = serviceStateIntent(subId = 1, emergencyOnly = true)
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                intent2,
+            )
+            runCurrent()
+
+            assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState)
+        }
+
     @Test
     @Ignore("b/333912012")
     fun testConnectionCache_clearsInvalidSubscriptions() =
@@ -1491,5 +1540,24 @@
                 whenever(it.transportInfo).thenReturn(WIFI_INFO_ACTIVE)
                 whenever(it.hasCapability(NET_CAPABILITY_VALIDATED)).thenReturn(true)
             }
+
+        /**
+         * To properly mimic telephony manager, create a service state, and then turn it into an
+         * intent
+         */
+        private fun serviceStateIntent(
+            subId: Int,
+            emergencyOnly: Boolean = false,
+        ): Intent {
+            val serviceState = ServiceState().apply { isEmergencyOnly = emergencyOnly }
+
+            val bundle = Bundle()
+            serviceState.fillInNotifierBundle(bundle)
+
+            return Intent(Intent.ACTION_SERVICE_STATE).apply {
+                putExtras(bundle)
+                putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId)
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 0f9cbfa..58d9ee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
@@ -888,6 +889,22 @@
             assertThat(interactor1).isSameInstanceAs(interactor2)
         }
 
+    @Test
+    fun deviceBasedEmergencyMode_emergencyCallsOnly_followsDeviceServiceStateFromRepo() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode)
+
+            connectionsRepository.deviceServiceState.value =
+                ServiceStateModel(isEmergencyOnly = true)
+
+            assertThat(latest).isTrue()
+
+            connectionsRepository.deviceServiceState.value =
+                ServiceStateModel(isEmergencyOnly = false)
+
+            assertThat(latest).isFalse()
+        }
+
     /**
      * Convenience method for creating a pair of subscriptions to test the filteredSubscriptions
      * flow.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index 405e3ed..d303976 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -22,6 +22,7 @@
 import com.android.internal.telephony.flags.Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.log.core.FakeLogBuffer
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.FakeDeviceBasedSatelliteRepository
@@ -71,6 +72,7 @@
                 deviceProvisioningInteractor,
                 wifiInteractor,
                 testScope.backgroundScope,
+                FakeLogBuffer.Factory.create(),
             )
     }
 
@@ -114,6 +116,7 @@
                     deviceProvisioningInteractor,
                     wifiInteractor,
                     testScope.backgroundScope,
+                    FakeLogBuffer.Factory.create(),
                 )
 
             val latest by collectLastValue(underTest.isSatelliteAllowed)
@@ -162,6 +165,7 @@
                     deviceProvisioningInteractor,
                     wifiInteractor,
                     testScope.backgroundScope,
+                    FakeLogBuffer.Factory.create(),
                 )
 
             val latest by collectLastValue(underTest.connectionState)
@@ -218,6 +222,7 @@
                     deviceProvisioningInteractor,
                     wifiInteractor,
                     testScope.backgroundScope,
+                    FakeLogBuffer.Factory.create(),
                 )
 
             val latest by collectLastValue(underTest.signalStrength)
@@ -238,25 +243,97 @@
 
     @Test
     @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    fun areAllConnectionsOutOfService_noConnections_yes() =
+    fun areAllConnectionsOutOfService_noConnections_noDeviceEmergencyCalls_yes() =
         testScope.runTest {
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
 
             // GIVEN, 0 connections
 
+            // GIVEN, device is not in emergency calls only mode
+            iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false
+
             // THEN the value is propagated to this interactor
             assertThat(latest).isTrue()
         }
 
     @Test
     @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    fun areAllConnectionsOutOfService_twoConnectionsOos_nonNtn_yes() =
+    fun areAllConnectionsOutOfService_noConnections_deviceEmergencyCalls_yes() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 0 connections
+
+            // GIVEN, device is in emergency calls only mode
+            iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun areAllConnectionsOutOfService_oneConnectionInService_thenLost_noDeviceEmergencyCalls_yes() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 1 connections
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            // GIVEN, no device-based emergency calls
+            iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false
+
+            // WHEN connection is in service
+            i1.isInService.value = true
+            i1.isEmergencyOnly.value = false
+            i1.isNonTerrestrial.value = false
+
+            // THEN we are considered NOT to be OOS
+            assertThat(latest).isFalse()
+
+            // WHEN the connection disappears
+            iconsInteractor.icons.value = listOf()
+
+            // THEN we are back to OOS
+            assertThat(latest).isTrue()
+        }
+
+    @Test
+    @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun areAllConnectionsOutOfService_oneConnectionInService_thenLost_deviceEmergencyCalls_no() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 1 connections
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            // GIVEN, device-based emergency calls
+            iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true
+
+            // WHEN one connection is in service
+            i1.isInService.value = true
+            i1.isEmergencyOnly.value = false
+            i1.isNonTerrestrial.value = false
+
+            // THEN we are considered NOT to be OOS
+            assertThat(latest).isFalse()
+
+            // WHEN the connection disappears
+            iconsInteractor.icons.value = listOf()
+
+            // THEN we are still NOT in OOS, due to device-based emergency calls
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun areAllConnectionsOutOfService_twoConnectionsOos_nonNtn_noDeviceEmergencyCalls_yes() =
         testScope.runTest {
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
 
             // GIVEN, 2 connections
             val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
             val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+            // GIVEN, no device-based emergency calls
+            iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false
 
             // WHEN all of the connections are OOS and none are NTN
             i1.isInService.value = false
@@ -272,13 +349,39 @@
 
     @Test
     @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    fun areAllConnectionsOutOfService_twoConnectionsOos_oneNtn_no() =
+    fun areAllConnectionsOutOfService_twoConnectionsOos_nonNtn_deviceEmergencyCalls_no() =
         testScope.runTest {
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
 
             // GIVEN, 2 connections
             val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
             val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+            // GIVEN, device-based emergency calls
+            iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true
+
+            // WHEN all of the connections are OOS and none are NTN
+            i1.isInService.value = false
+            i1.isEmergencyOnly.value = false
+            i1.isNonTerrestrial.value = false
+            i2.isInService.value = false
+            i2.isEmergencyOnly.value = false
+            i2.isNonTerrestrial.value = false
+
+            // THEN we are not considered OOS due to device based emergency calling
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun areAllConnectionsOutOfService_twoConnectionsOos_noDeviceEmergencyCalls_oneNtn_no() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 2 connections
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+            // GIVEN, no device-based emergency calls
+            iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false
 
             // WHEN all of the connections are OOS and one is NTN
             i1.isInService.value = false
@@ -296,12 +399,14 @@
 
     @Test
     @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    fun areAllConnectionsOutOfService_oneConnectionOos_nonNtn_yes() =
+    fun areAllConnectionsOutOfService_oneConnectionOos_noDeviceEmergencyCalls_nonNtn_yes() =
         testScope.runTest {
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
 
             // GIVEN, 1 connection
             val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            // GIVEN, no device-based emergency calls
+            iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false
 
             // WHEN all of the connections are OOS
             i1.isInService.value = false
@@ -314,7 +419,27 @@
 
     @Test
     @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    fun areAllConnectionsOutOfService_oneConnectionOos_ntn_yes() =
+    fun areAllConnectionsOutOfService_oneConnectionOos_nonNtn_no() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 1 connection
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            // GIVEN, device-based emergency calls
+            iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true
+
+            // WHEN all of the connections are OOS
+            i1.isInService.value = false
+            i1.isEmergencyOnly.value = false
+            i1.isNonTerrestrial.value = false
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun areAllConnectionsOutOfService_oneConnectionOos_ntn_no() =
         testScope.runTest {
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
 
@@ -416,6 +541,7 @@
                     deviceProvisioningInteractor,
                     wifiInteractor,
                     testScope.backgroundScope,
+                    FakeLogBuffer.Factory.create(),
                 )
 
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index ceaae9e..43b9568 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -75,6 +75,7 @@
                 deviceProvisioningInteractor,
                 wifiInteractor,
                 testScope.backgroundScope,
+                FakeLogBuffer.Factory.create(),
             )
 
         underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index ed7c956..a5e7a67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -995,9 +995,11 @@
                 .setResourceValue(any(String.class), eq(TYPE_INT_COLOR_ARGB8), anyInt(), eq(null));
         // All dynamic colors were added twice: light and dark them
         // All fixed colors were added once
+        // All custom dynamic tokens added twice
         verify(dynamic, times(
                 DynamicColors.allDynamicColorsMapped(false).size() * 2
-                        + DynamicColors.getFixedColorsMapped(false).size())
+                        + DynamicColors.getFixedColorsMapped(false).size()
+                        + DynamicColors.getCustomColorsMapped(false).size() * 2)
         ).setResourceValue(any(String.class), eq(TYPE_INT_COLOR_ARGB8), anyInt(), eq(null));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
index 948670f..01795e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.user.domain.interactor
 
 import android.app.admin.DevicePolicyManager
+import android.content.Context
 import android.content.pm.UserInfo
 import android.os.UserHandle
 import android.os.UserManager
@@ -59,6 +60,7 @@
     @Mock private lateinit var switchUser: (Int) -> Unit
     @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
     @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
+    @Mock private lateinit var otherContext: Context
 
     private lateinit var underTest: GuestUserInteractor
 
@@ -74,28 +76,30 @@
         repository = FakeUserRepository()
         repository.setUserInfos(ALL_USERS)
 
-        underTest =
-            GuestUserInteractor(
-                applicationContext = context,
-                applicationScope = scope,
-                mainDispatcher = IMMEDIATE,
-                backgroundDispatcher = IMMEDIATE,
-                manager = manager,
-                repository = repository,
-                deviceProvisionedController = deviceProvisionedController,
-                devicePolicyManager = devicePolicyManager,
-                refreshUsersScheduler =
-                    RefreshUsersScheduler(
-                        applicationScope = scope,
-                        mainDispatcher = IMMEDIATE,
-                        repository = repository,
-                    ),
-                uiEventLogger = uiEventLogger,
-                resumeSessionReceiver = resumeSessionReceiver,
-                resetOrExitSessionReceiver = resetOrExitSessionReceiver,
-            )
+        underTest = initGuestUserInteractor(context)
     }
 
+    private fun initGuestUserInteractor(context: Context) =
+        GuestUserInteractor(
+            applicationContext = context,
+            applicationScope = scope,
+            mainDispatcher = IMMEDIATE,
+            backgroundDispatcher = IMMEDIATE,
+            manager = manager,
+            repository = repository,
+            deviceProvisionedController = deviceProvisionedController,
+            devicePolicyManager = devicePolicyManager,
+            refreshUsersScheduler =
+                RefreshUsersScheduler(
+                    applicationScope = scope,
+                    mainDispatcher = IMMEDIATE,
+                    repository = repository,
+                ),
+            uiEventLogger = uiEventLogger,
+            resumeSessionReceiver = resumeSessionReceiver,
+            resetOrExitSessionReceiver = resetOrExitSessionReceiver,
+        )
+
     @Test
     fun registersBroadcastReceivers() {
         verify(resumeSessionReceiver).register()
@@ -103,6 +107,16 @@
     }
 
     @Test
+    fun registersBroadcastReceiversOnlyForSystemUser() {
+        for (i in 1..5) {
+            whenever(otherContext.userId).thenReturn(UserHandle.MIN_SECONDARY_USER_ID + i)
+            initGuestUserInteractor(otherContext)
+        }
+        verify(resumeSessionReceiver).register()
+        verify(resetOrExitSessionReceiver).register()
+    }
+
+    @Test
     fun onDeviceBootCompleted_allowedToAdd_createGuest() =
         runBlocking(IMMEDIATE) {
             setAllowedToAdd()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index 3dee093..96c6eb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -887,6 +887,46 @@
     }
 
     @Test
+    fun removeGuestUser_shouldNotShowExitGuestDialog() {
+        createUserInteractor()
+        testScope.runTest {
+            val (userInfo, guestUserInfo) = createUserInfos(count = 2, includeGuest = true)
+            userRepository.setUserInfos(listOf(userInfo, guestUserInfo))
+            userRepository.setSelectedUserInfo(guestUserInfo)
+
+            whenever(manager.markGuestForDeletion(guestUserInfo.id)).thenReturn(true)
+            underTest.removeGuestUser(guestUserInfo.id, userInfo.id)
+            runCurrent()
+
+            verify(manager).markGuestForDeletion(guestUserInfo.id)
+            verify(activityManager).switchUser(userInfo.id)
+            assertThat(collectLastValue(underTest.dialogShowRequests)()).isNull()
+        }
+    }
+
+    @Test
+    fun resetGuestUser_shouldNotShowExitGuestDialog() {
+        createUserInteractor()
+        testScope.runTest {
+            val (userInfo, guestUserInfo) = createUserInfos(count = 2, includeGuest = true)
+            val otherGuestUserInfo = createUserInfos(count = 1, includeGuest = true)[0]
+            userRepository.setUserInfos(listOf(userInfo, guestUserInfo))
+            userRepository.setSelectedUserInfo(guestUserInfo)
+
+            whenever(manager.markGuestForDeletion(guestUserInfo.id)).thenReturn(true)
+            whenever(manager.createGuest(any())).thenReturn(otherGuestUserInfo)
+            underTest.removeGuestUser(guestUserInfo.id, UserHandle.USER_NULL)
+            runCurrent()
+
+            verify(manager).markGuestForDeletion(guestUserInfo.id)
+            verify(manager).createGuest(any())
+            verify(activityManager).switchUser(otherGuestUserInfo.id)
+            assertThat(collectLastValue(underTest.dialogShowRequests)())
+                .isEqualTo(ShowDialogRequestModel.ShowUserCreationDialog(isGuest = true))
+        }
+    }
+
+    @Test
     fun showUserSwitcher_fullScreenDisabled_showsDialogSwitcher() {
         createUserInteractor()
         testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
index 31848a6..e1dcb14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
@@ -1,8 +1,8 @@
 package com.android.systemui.util
 
 import android.graphics.Rect
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.wm.shell.common.FloatingContentCoordinator
@@ -14,7 +14,7 @@
 import org.junit.runner.RunWith
 
 @TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class FloatingContentCoordinatorTest : SysuiTestCase() {
 
@@ -198,12 +198,11 @@
     }
 
     /**
-     * Helper class that uses [floatingCoordinator.findAreaForContentVertically] to move a
-     * Rect when needed.
+     * Helper class that uses [floatingCoordinator.findAreaForContentVertically] to move a Rect when
+     * needed.
      */
-    inner class FloatingRect(
-        private val underlyingRect: Rect
-    ) : FloatingContentCoordinator.FloatingContent {
+    inner class FloatingRect(private val underlyingRect: Rect) :
+        FloatingContentCoordinator.FloatingContent {
         override fun moveToBounds(bounds: Rect) {
             underlyingRect.set(bounds)
         }
@@ -216,4 +215,4 @@
             return underlyingRect
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
index 436f5b8..457f2bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
@@ -19,12 +19,13 @@
 import android.content.BroadcastReceiver
 import android.content.IntentFilter
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import androidx.lifecycle.Observer
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
+import java.util.concurrent.Executor
 import org.junit.After
 import org.junit.Assert.assertTrue
 import org.junit.Before
@@ -38,10 +39,9 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class RingerModeLiveDataTest : SysuiTestCase() {
 
@@ -52,16 +52,11 @@
         private val INTENT = "INTENT"
     }
 
-    @Mock
-    private lateinit var broadcastDispatcher: BroadcastDispatcher
-    @Mock
-    private lateinit var valueSupplier: () -> Int
-    @Mock
-    private lateinit var observer: Observer<Int>
-    @Captor
-    private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
-    @Captor
-    private lateinit var intentFilterCaptor: ArgumentCaptor<IntentFilter>
+    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock private lateinit var valueSupplier: () -> Int
+    @Mock private lateinit var observer: Observer<Int>
+    @Captor private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
+    @Captor private lateinit var intentFilterCaptor: ArgumentCaptor<IntentFilter>
 
     // Run everything immediately
     private val executor = Executor { it.run() }
@@ -88,14 +83,14 @@
     fun testOnActive_broadcastRegistered() {
         liveData.observeForever(observer)
         verify(broadcastDispatcher)
-                .registerReceiver(any(), any(), eq(executor), eq(UserHandle.ALL), anyInt(), any())
+            .registerReceiver(any(), any(), eq(executor), eq(UserHandle.ALL), anyInt(), any())
     }
 
     @Test
     fun testOnActive_intentFilterHasIntent() {
         liveData.observeForever(observer)
-        verify(broadcastDispatcher).registerReceiver(any(), capture(intentFilterCaptor), any(),
-                any(), anyInt(), any())
+        verify(broadcastDispatcher)
+            .registerReceiver(any(), capture(intentFilterCaptor), any(), any(), anyInt(), any())
         assertTrue(intentFilterCaptor.value.hasAction(INTENT))
     }
 
@@ -111,4 +106,4 @@
         liveData.removeObserver(observer)
         verify(broadcastDispatcher).unregisterReceiver(any())
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
index b13cb72..6271904 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
@@ -19,10 +19,10 @@
 import android.app.WallpaperInfo
 import android.app.WallpaperManager
 import android.os.IBinder
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.view.View
 import android.view.ViewRootImpl
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.eq
@@ -32,36 +32,30 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.any
 import org.mockito.Mockito.anyFloat
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.doThrow
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper
 @SmallTest
 class WallpaperControllerTest : SysuiTestCase() {
 
-    @Mock
-    private lateinit var wallpaperManager: WallpaperManager
-    @Mock
-    private lateinit var root: View
-    @Mock
-    private lateinit var viewRootImpl: ViewRootImpl
-    @Mock
-    private lateinit var windowToken: IBinder
+    @Mock private lateinit var wallpaperManager: WallpaperManager
+    @Mock private lateinit var root: View
+    @Mock private lateinit var viewRootImpl: ViewRootImpl
+    @Mock private lateinit var windowToken: IBinder
     private val wallpaperRepository = FakeWallpaperRepository()
 
-    @JvmField
-    @Rule
-    val mockitoRule = MockitoJUnit.rule()
+    @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
 
     private lateinit var wallaperController: WallpaperController
 
@@ -92,9 +86,7 @@
 
     @Test
     fun setUnfoldTransitionZoom_defaultUnfoldTransitionIsDisabled_doesNotUpdateWallpaperZoom() {
-        wallpaperRepository.wallpaperInfo.value = createWallpaperInfo(
-            useDefaultTransition = false
-        )
+        wallpaperRepository.wallpaperInfo.value = createWallpaperInfo(useDefaultTransition = false)
 
         wallaperController.setUnfoldTransitionZoom(0.5f)
 
@@ -130,7 +122,8 @@
 
     @Test
     fun setNotificationZoom_exceptionWhenUpdatingZoom_doesNotFail() {
-        doThrow(IllegalArgumentException("test exception")).`when`(wallpaperManager)
+        doThrow(IllegalArgumentException("test exception"))
+            .`when`(wallpaperManager)
             .setWallpaperZoomOut(any(), anyFloat())
 
         wallaperController.setNotificationShadeZoom(0.5f)
@@ -140,8 +133,7 @@
 
     private fun createWallpaperInfo(useDefaultTransition: Boolean = true): WallpaperInfo {
         val info = mock(WallpaperInfo::class.java)
-        whenever(info.shouldUseDefaultUnfoldTransition())
-            .thenReturn(useDefaultTransition)
+        whenever(info.shouldUseDefaultUnfoldTransition()).thenReturn(useDefaultTransition)
         return info
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
index 92afb03..b26598c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
@@ -16,13 +16,16 @@
 
 package com.android.systemui.util.animation
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
-import org.junit.Test
 import java.lang.IllegalArgumentException
+import org.junit.runner.RunWith
+import org.junit.Test
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class AnimationUtilTest : SysuiTestCase() {
     @Test
     fun getMsForFrames_5frames_returns83() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
index 9dfa14d..7dfac0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
@@ -22,8 +22,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -39,7 +38,7 @@
 import java.util.List;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class FakeExecutorTest extends SysuiTestCase {
     @Before
     public void setUp() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
index 78fc680..48fb745 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
@@ -24,8 +24,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -39,7 +38,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class MessageRouterImplTest extends SysuiTestCase {
     private static final int MESSAGE_A = 0;
     private static final int MESSAGE_B = 1;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
index d1d2598..7ec420f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
@@ -15,7 +15,7 @@
  */
 package com.android.systemui.util.concurrency
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.time.FakeSystemClock
@@ -26,7 +26,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class MockExecutorHandlerTest : SysuiTestCase() {
     /** Test FakeExecutor that receives non-delayed items to execute. */
     @Test
@@ -141,6 +141,88 @@
         assertEquals(3, runnable.mRunCount)
     }
 
+    @Test
+    fun testRemoveCallback_postDelayed() {
+        val clock = FakeSystemClock()
+        val fakeExecutor = FakeExecutor(clock)
+        val handler = mockExecutorHandler(fakeExecutor)
+        val runnable = RunnableImpl()
+
+        handler.postDelayed(runnable, 50)
+        handler.postDelayed(runnable, 150)
+        fakeExecutor.advanceClockToNext()
+        fakeExecutor.runAllReady()
+
+        assertEquals(1, runnable.mRunCount)
+        assertEquals(1, fakeExecutor.numPending())
+
+        handler.removeCallbacks(runnable)
+        assertEquals(0, fakeExecutor.numPending())
+
+        assertEquals(1, runnable.mRunCount)
+    }
+
+    @Test
+    fun testRemoveCallback_postAtTime() {
+        val clock = FakeSystemClock()
+        val fakeExecutor = FakeExecutor(clock)
+        val handler = mockExecutorHandler(fakeExecutor)
+        val runnable = RunnableImpl()
+        assertEquals(10000, clock.uptimeMillis())
+
+        handler.postAtTime(runnable, 10050)
+        handler.postAtTime(runnable, 10150)
+        fakeExecutor.advanceClockToNext()
+        fakeExecutor.runAllReady()
+
+        assertEquals(1, runnable.mRunCount)
+        assertEquals(1, fakeExecutor.numPending())
+
+        handler.removeCallbacks(runnable)
+        assertEquals(0, fakeExecutor.numPending())
+
+        assertEquals(1, runnable.mRunCount)
+    }
+
+    @Test
+    fun testRemoveCallback_mixed_allRemoved() {
+        val clock = FakeSystemClock()
+        val fakeExecutor = FakeExecutor(clock)
+        val handler = mockExecutorHandler(fakeExecutor)
+        val runnable = RunnableImpl()
+        assertEquals(10000, clock.uptimeMillis())
+
+        handler.postAtTime(runnable, 10050)
+        handler.postDelayed(runnable, 150)
+
+        handler.removeCallbacks(runnable)
+        assertEquals(0, fakeExecutor.numPending())
+
+        fakeExecutor.advanceClockToLast()
+        fakeExecutor.runAllReady()
+        assertEquals(0, runnable.mRunCount)
+    }
+
+    @Test
+    fun testRemoveCallback_differentRunnables_onlyMatchingRemoved() {
+        val clock = FakeSystemClock()
+        val fakeExecutor = FakeExecutor(clock)
+        val handler = mockExecutorHandler(fakeExecutor)
+        val runnable1 = RunnableImpl()
+        val runnable2 = RunnableImpl()
+
+        handler.postDelayed(runnable1, 50)
+        handler.postDelayed(runnable2, 150)
+
+        handler.removeCallbacks(runnable1)
+        assertEquals(1, fakeExecutor.numPending())
+
+        fakeExecutor.advanceClockToLast()
+        fakeExecutor.runAllReady()
+        assertEquals(0, runnable1.mRunCount)
+        assertEquals(1, runnable2.mRunCount)
+    }
+
     /**
      * Verifies that `Handler.removeMessages`, which doesn't make sense with executor backing,
      * causes an error in the test (rather than failing silently like most mocks).
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
index 00f37ae..13fff29d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
@@ -18,8 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -30,7 +29,7 @@
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class RepeatableExecutorTest extends SysuiTestCase {
 
     private static final int DELAY = 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
index b367a60..37015e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
@@ -23,8 +23,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.CoreStartable;
@@ -44,7 +43,7 @@
 import java.util.Set;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ConditionalCoreStartableTest extends SysuiTestCase {
     public static class FakeConditionalCoreStartable extends ConditionalCoreStartable {
         interface Callback {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
index ac357ea..b8f5815 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
@@ -4,7 +4,7 @@
 import android.graphics.Bitmap
 import android.graphics.drawable.BitmapDrawable
 import android.graphics.drawable.ShapeDrawable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -12,7 +12,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class DrawableSizeTest : SysuiTestCase() {
 
@@ -32,14 +32,11 @@
 
     @Test
     fun testDownscaleToSize_drawableSmallerThanRequirement_unchanged() {
-        val drawable = BitmapDrawable(resources,
-                Bitmap.createBitmap(
-                        resources.displayMetrics,
-                        150,
-                        150,
-                        Bitmap.Config.ARGB_8888
-                )
-        )
+        val drawable =
+            BitmapDrawable(
+                resources,
+                Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888)
+            )
         val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300)
         assertThat(result).isSameInstanceAs(drawable)
     }
@@ -48,14 +45,11 @@
     fun testDownscaleToSize_drawableLargerThanRequirementWithDensity_resized() {
         // This bitmap would actually fail to resize if the method doesn't check for
         // bitmap dimensions inside drawable.
-        val drawable = BitmapDrawable(resources,
-                Bitmap.createBitmap(
-                        resources.displayMetrics,
-                        150,
-                        75,
-                        Bitmap.Config.ARGB_8888
-                )
-        )
+        val drawable =
+            BitmapDrawable(
+                resources,
+                Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888)
+            )
 
         val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75)
         assertThat(result).isNotSameInstanceAs(drawable)
@@ -65,9 +59,9 @@
 
     @Test
     fun testDownscaleToSize_drawableAnimated_unchanged() {
-        val drawable = resources.getDrawable(android.R.drawable.stat_sys_download,
-                resources.newTheme())
+        val drawable =
+            resources.getDrawable(android.R.drawable.stat_sys_download, resources.newTheme())
         val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1)
         assertThat(result).isSameInstanceAs(drawable)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 7d0d57b..e2ce50c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.util.kotlin
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.time.FakeSystemClock
@@ -47,7 +47,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class PairwiseFlowTest : SysuiTestCase() {
     @Test
     fun simple() = runBlocking {
@@ -89,7 +89,9 @@
                         initRun = true
                         "initial"
                     }
-                ) { prev: String, next: String -> "$prev|$next" }
+                ) { prev: String, next: String ->
+                    "$prev|$next"
+                }
             )
             .emitsExactly("initial|val1", "val1|val2")
         assertThat(initRun).isTrue()
@@ -104,7 +106,9 @@
                         initRun = true
                         "initial"
                     }
-                ) { prev: String, next: String -> "$prev|$next" }
+                ) { prev: String, next: String ->
+                    "$prev|$next"
+                }
             )
             .emitsNothing()
         // Even though the flow will not emit anything, the initial value function should still get
@@ -120,7 +124,9 @@
                 initRun = true
                 "initial"
             }
-        ) { prev: String, next: String -> "$prev|$next" }
+        ) { prev: String, next: String ->
+            "$prev|$next"
+        }
 
         // Since the flow isn't collected, ensure [initialValueFun] isn't run.
         assertThat(initRun).isFalse()
@@ -146,7 +152,7 @@
 }
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class SetChangesFlowTest : SysuiTestCase() {
     @Test
     fun simple() = runBlocking {
@@ -198,7 +204,7 @@
 }
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class SampleFlowTest : SysuiTestCase() {
     @Test
     fun simple() = runBlocking {
@@ -240,7 +246,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ThrottleFlowTest : SysuiTestCase() {
 
     @Test
@@ -248,13 +254,16 @@
         // Arrange
         val choreographer = createChoreographer(this)
         val output = mutableListOf<Int>()
-        val collectJob = backgroundScope.launch {
-            flow {
-                emit(1)
-                delay(1000)
-                emit(2)
-            }.throttle(1000, choreographer.fakeClock).toList(output)
-        }
+        val collectJob =
+            backgroundScope.launch {
+                flow {
+                        emit(1)
+                        delay(1000)
+                        emit(2)
+                    }
+                    .throttle(1000, choreographer.fakeClock)
+                    .toList(output)
+            }
 
         // Act
         choreographer.advanceAndRun(0)
@@ -283,13 +292,16 @@
         // Arrange
         val choreographer = createChoreographer(this)
         val output = mutableListOf<Int>()
-        val collectJob = backgroundScope.launch {
-            flow {
-                emit(1)
-                delay(500)
-                emit(2)
-            }.throttle(1000, choreographer.fakeClock).toList(output)
-        }
+        val collectJob =
+            backgroundScope.launch {
+                flow {
+                        emit(1)
+                        delay(500)
+                        emit(2)
+                    }
+                    .throttle(1000, choreographer.fakeClock)
+                    .toList(output)
+            }
 
         // Act
         choreographer.advanceAndRun(0)
@@ -319,15 +331,18 @@
         // Arrange
         val choreographer = createChoreographer(this)
         val output = mutableListOf<Int>()
-        val collectJob = backgroundScope.launch {
-            flow {
-                emit(1)
-                delay(500)
-                emit(2)
-                delay(500)
-                emit(3)
-            }.throttle(1000, choreographer.fakeClock).toList(output)
-        }
+        val collectJob =
+            backgroundScope.launch {
+                flow {
+                        emit(1)
+                        delay(500)
+                        emit(2)
+                        delay(500)
+                        emit(3)
+                    }
+                    .throttle(1000, choreographer.fakeClock)
+                    .toList(output)
+            }
 
         // Act
         choreographer.advanceAndRun(0)
@@ -357,15 +372,18 @@
         // Arrange
         val choreographer = createChoreographer(this)
         val output = mutableListOf<Int>()
-        val collectJob = backgroundScope.launch {
-            flow {
-                emit(1)
-                delay(500)
-                emit(2)
-                delay(250)
-                emit(3)
-            }.throttle(1000, choreographer.fakeClock).toList(output)
-        }
+        val collectJob =
+            backgroundScope.launch {
+                flow {
+                        emit(1)
+                        delay(500)
+                        emit(2)
+                        delay(250)
+                        emit(3)
+                    }
+                    .throttle(1000, choreographer.fakeClock)
+                    .toList(output)
+            }
 
         // Act
         choreographer.advanceAndRun(0)
@@ -391,15 +409,16 @@
         collectJob.cancel()
     }
 
-    private fun createChoreographer(testScope: TestScope) = object {
-        val fakeClock = FakeSystemClock()
+    private fun createChoreographer(testScope: TestScope) =
+        object {
+            val fakeClock = FakeSystemClock()
 
-        fun advanceAndRun(millis: Long) {
-            fakeClock.advanceTime(millis)
-            testScope.advanceTimeBy(millis)
-            testScope.runCurrent()
+            fun advanceAndRun(millis: Long) {
+                fakeClock.advanceTime(millis)
+                testScope.advanceTimeBy(millis)
+                testScope.runCurrent()
+            }
         }
-    }
 }
 
 private fun <T> assertThatFlow(flow: Flow<T>) =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
index 4ca1fd3..c31b287 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.util.kotlin
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import java.util.concurrent.atomic.AtomicLong
@@ -31,43 +31,42 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class IpcSerializerTest : SysuiTestCase() {
 
     private val serializer = IpcSerializer()
 
     @Ignore("b/253046405")
     @Test
-    fun serializeManyIncomingIpcs(): Unit = runBlocking(Dispatchers.Main.immediate) {
-        val processor = launch(start = CoroutineStart.LAZY) { serializer.process() }
-        withContext(Dispatchers.IO) {
-            val lastEvaluatedTime = AtomicLong(System.currentTimeMillis())
-            // First, launch many serialization requests in parallel
-            repeat(100_000) {
-                launch(Dispatchers.Unconfined) {
-                    val enqueuedTime = System.currentTimeMillis()
-                    serializer.runSerialized {
-                        val last = lastEvaluatedTime.getAndSet(enqueuedTime)
-                        assertTrue(
-                            "expected $last less than or equal to $enqueuedTime ",
-                            last <= enqueuedTime,
-                        )
+    fun serializeManyIncomingIpcs(): Unit =
+        runBlocking(Dispatchers.Main.immediate) {
+            val processor = launch(start = CoroutineStart.LAZY) { serializer.process() }
+            withContext(Dispatchers.IO) {
+                val lastEvaluatedTime = AtomicLong(System.currentTimeMillis())
+                // First, launch many serialization requests in parallel
+                repeat(100_000) {
+                    launch(Dispatchers.Unconfined) {
+                        val enqueuedTime = System.currentTimeMillis()
+                        serializer.runSerialized {
+                            val last = lastEvaluatedTime.getAndSet(enqueuedTime)
+                            assertTrue(
+                                "expected $last less than or equal to $enqueuedTime ",
+                                last <= enqueuedTime,
+                            )
+                        }
                     }
                 }
+                // Then, process them all in the order they came in.
+                processor.start()
             }
-            // Then, process them all in the order they came in.
-            processor.start()
+            // All done, stop processing
+            processor.cancel()
         }
-        // All done, stop processing
-        processor.cancel()
-    }
 
     @Test(timeout = 5000)
     fun serializeOnOneThread_doesNotDeadlock() = runBlocking {
         val job = launch { serializer.process() }
-        repeat(100) {
-            serializer.runSerializedBlocking { }
-        }
+        repeat(100) { serializer.runSerializedBlocking {} }
         job.cancel()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
index 2013bb0..8bfff9c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
@@ -27,13 +27,13 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 internal class PackageManagerExtComponentEnabledTest(private val testCase: TestCase) :
     SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
index 6848b83..b2f7c1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.util.kotlin
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -28,7 +28,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class RaceSuspendTest : SysuiTestCase() {
     @Test
     fun raceSimple() = runBlocking {
@@ -46,10 +46,11 @@
     @Test
     fun raceImmediate() = runBlocking {
         assertThat(
-            race<Int>(
-                { 1 },
-                { 2 },
+                race<Int>(
+                    { 1 },
+                    { 2 },
+                )
             )
-        ).isEqualTo(1)
+            .isEqualTo(1)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
index 84129be..300c298 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
@@ -26,9 +26,9 @@
 
 import android.content.res.Resources;
 import android.hardware.Sensor;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -46,7 +46,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class PostureDependentProximitySensorTest extends SysuiTestCase {
     @Mock private Resources mResources;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
index 19dbf9a..5dd008a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
@@ -23,9 +23,9 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
 import java.util.function.Consumer;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class ProximityCheckTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
index 5e75578..0eab74e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
@@ -24,9 +24,9 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class ProximitySensorImplDualTest extends SysuiTestCase {
     private ProximitySensor mProximitySensor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
index 752cd32..f44c842 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
@@ -21,9 +21,9 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
  * Tests for ProximitySensor that rely on a single hardware sensor.
  */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class ProximitySensorImplSingleTest extends SysuiTestCase {
     private ProximitySensor mProximitySensor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
index 8d26c87..a54afad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
@@ -30,8 +30,8 @@
 import android.content.pm.UserInfo;
 import android.os.IBinder;
 import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -49,7 +49,7 @@
 import java.util.Objects;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ObservableServiceConnectionTest extends SysuiTestCase {
     static class Foo {
         int mValue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
index a2fd288..a70b00c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
@@ -24,8 +24,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -38,7 +38,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class PackageObserverTest extends SysuiTestCase {
     @Mock
     Context mContext;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
index 55c49ee..ef10fdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
@@ -19,8 +19,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -37,7 +36,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class PersistentConnectionManagerTest extends SysuiTestCase {
     private static final int MAX_RETRIES = 5;
     private static final int RETRY_DELAY_MS = 1000;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
index f65caee2..99f6303 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -28,8 +28,8 @@
 import android.database.ContentObserver;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -44,7 +44,7 @@
 import java.util.Map;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class FakeSettingsTest extends SysuiTestCase {
     @Mock
     ContentObserver mContentObserver;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
index 913759f..88b2630 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.util.settings.repository
 
 import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -33,11 +34,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() {
 
     private val dispatcher = StandardTestDispatcher()
@@ -48,11 +48,12 @@
 
     @Before
     fun setup() {
-        repository = UserAwareSecureSettingsRepositoryImpl(
-            secureSettings,
-            userRepository,
-            dispatcher,
-        )
+        repository =
+            UserAwareSecureSettingsRepositoryImpl(
+                secureSettings,
+                userRepository,
+                dispatcher,
+            )
         userRepository.setUserInfos(USER_INFOS)
         setSettingValueForUser(enabled = true, userInfo = SETTING_ENABLED_USER)
         setSettingValueForUser(enabled = false, userInfo = SETTING_DISABLED_USER)
@@ -105,4 +106,4 @@
         val SETTING_DISABLED_USER = UserInfo(/* id= */ 1, "user2", /* flags= */ 0)
         val USER_INFOS = listOf(SETTING_ENABLED_USER, SETTING_DISABLED_USER)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
index 94100fe..6637d5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
@@ -17,7 +17,7 @@
 
 package com.android.systemui.util.ui
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -30,7 +30,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class AnimatedValueTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
index e3cd9b2..3dcb828 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
@@ -19,17 +19,20 @@
 import android.graphics.Rect
 import android.view.View
 import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.`when`
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class ViewUtilTest : SysuiTestCase() {
     private val viewUtil = ViewUtil()
     private lateinit var view: View
@@ -45,11 +48,13 @@
         location[1] = VIEW_TOP
         `when`(view.locationOnScreen).thenReturn(location)
         doAnswer { invocation ->
-            val pos = invocation.arguments[0] as IntArray
-            pos[0] = VIEW_LEFT
-            pos[1] = VIEW_TOP
-            null
-        }.`when`(view).getLocationInWindow(any())
+                val pos = invocation.arguments[0] as IntArray
+                pos[0] = VIEW_LEFT
+                pos[1] = VIEW_TOP
+                null
+            }
+            .`when`(view)
+            .getLocationInWindow(any())
     }
 
     @Test
@@ -59,9 +64,8 @@
 
     @Test
     fun touchIsWithinView_onTopLeftCorner_returnsTrue() {
-        assertThat(viewUtil.touchIsWithinView(
-            view, VIEW_LEFT.toFloat(), VIEW_TOP.toFloat())
-        ).isTrue()
+        assertThat(viewUtil.touchIsWithinView(view, VIEW_LEFT.toFloat(), VIEW_TOP.toFloat()))
+            .isTrue()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index ed07ad2..207c35d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -35,15 +35,18 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
 import java.util.List;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+
 @SmallTest
-@RunWith(Parameterized.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 public class WakeLockTest extends SysuiTestCase {
 
-    @Parameterized.Parameters(name = "{0}")
+    @Parameters(name = "{0}")
     public static List<FlagsParameterization> getFlags() {
         return FlagsParameterization.allCombinationsOf(
                 Flags.FLAG_DELAYED_WAKELOCK_RELEASE_ON_BACKGROUND_THREAD);
@@ -114,4 +117,4 @@
         // shouldn't throw an exception on production builds
         mWakeLock.release(WHY);
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 3b468aa..9864439 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -65,7 +65,6 @@
 import android.widget.SeekBar;
 
 import androidx.test.core.view.MotionEventBuilder;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.jank.InteractionJankMonitor;
@@ -273,54 +272,30 @@
 
     @Test
     @DisableFlags(FLAG_HAPTIC_VOLUME_SLIDER)
-    public void testVolumeChange_noSliderHaptics_doesNotDeliverOnProgressChangedHaptics() {
-        final State shellState = createShellState();
-        VolumeDialogController.StreamState musicStreamState =
-                shellState.states.get(AudioSystem.STREAM_MUSIC);
+    public void addSliderHaptics_withHapticsDisabled_doesNotDeliverOnProgressChangedHaptics() {
+        // GIVEN that the slider haptics flag is disabled and we try to add haptics to volume rows
+        mDialog.addSliderHapticsToRows();
 
-        mDialog.show(SHOW_REASON_UNKNOWN);
-        mTestableLooper.processMessages(1); //Only the SHOW message
-        mDialog.removeDismissMessages(); // Temporarily remove the rescheduled DISMISS
+        // WHEN haptics try to be delivered to a volume stream
+        boolean canDeliverHaptics =
+                mDialog.canDeliverProgressHapticsToStream(AudioSystem.STREAM_MUSIC, true, 50);
 
-        // Change the volume two times
-        musicStreamState.level += 10;
-        mDialog.onStateChangedH(shellState);
-        musicStreamState.level += 10;
-        mDialog.onStateChangedH(shellState);
-
-        // expected: the type of the latest progress haptics for the stream should be DISABLED
-        int type = mDialog.progressHapticsForStream(AudioSystem.STREAM_MUSIC);
-        assertEquals(VolumeDialogImpl.PROGRESS_HAPTICS_DISABLED, type);
-
-        mDialog.dismiss(DISMISS_REASON_UNKNOWN); // Dismiss
-        mTestableLooper.processAllMessages();
+        // THEN the result is that haptics are not successfully delivered
+        assertFalse(canDeliverHaptics);
     }
 
-    @Test @FlakyTest(bugId = 329099861)
+    @Test
     @EnableFlags(FLAG_HAPTIC_VOLUME_SLIDER)
-    public void testVolumeChange_withSliderHaptics_deliversOnProgressChangedHapticsEagerly() {
-        // create haptic plugins on the rows with the flag enabled
+    public void addSliderHaptics_withHapticsEnabled_canDeliverOnProgressChangedHaptics() {
+        // GIVEN that the slider haptics flag is enabled and we try to add haptics to volume rows
         mDialog.addSliderHapticsToRows();
-        final State shellState = createShellState();
-        VolumeDialogController.StreamState musicStreamState =
-                shellState.states.get(AudioSystem.STREAM_MUSIC);
 
-        mDialog.show(SHOW_REASON_UNKNOWN);
-        mTestableLooper.processMessages(1); //Only the SHOW message
-        mDialog.removeDismissMessages(); // Temporarily remove the rescheduled DISMISS
+        // WHEN haptics try to be delivered to a volume stream
+        boolean canDeliverHaptics =
+                mDialog.canDeliverProgressHapticsToStream(AudioSystem.STREAM_MUSIC, true, 50);
 
-        // Change the volume two times
-        musicStreamState.level += 10;
-        mDialog.onStateChangedH(shellState);
-        musicStreamState.level += 10;
-        mDialog.onStateChangedH(shellState);
-
-        // expected: the type of the latest progress haptics for the stream should be EAGER
-        int type = mDialog.progressHapticsForStream(AudioSystem.STREAM_MUSIC);
-        assertEquals(VolumeDialogImpl.PROGRESS_HAPTICS_EAGER, type);
-
-        mDialog.dismiss(DISMISS_REASON_UNKNOWN); // Dismiss
-        mTestableLooper.processAllMessages();
+        // THEN the result is that haptics are successfully delivered
+        assertTrue(canDeliverHaptics);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 56e5e29..df78110 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -27,6 +27,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING;
+import static com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -138,7 +139,6 @@
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -2142,6 +2142,112 @@
         assertThat(mBubbleController.getLayerView().isExpanded()).isFalse();
     }
 
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void dragBubbleBarBubble_selectedBubble_expandedViewCollapsesDuringDrag() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        // Add 2 bubbles
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow2);
+        mBubbleController.updateBubble(mBubbleEntry);
+        mBubbleController.updateBubble(mBubbleEntry2);
+
+        // Select first bubble
+        mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), new Rect());
+        assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey());
+        assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+
+        // Drag first bubble, bubble should collapse
+        mBubbleController.startBubbleDrag(mBubbleEntry.getKey());
+        assertThat(mBubbleController.getLayerView().isExpanded()).isFalse();
+
+        // Stop dragging, first bubble should be expanded
+        mBubbleController.stopBubbleDrag(BubbleBarLocation.LEFT);
+        assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey());
+        assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+    }
+
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void dragBubbleBarBubble_unselectedBubble_expandedViewCollapsesDuringDrag() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        // Add 2 bubbles
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow2);
+        mBubbleController.updateBubble(mBubbleEntry);
+        mBubbleController.updateBubble(mBubbleEntry2);
+
+        // Select first bubble
+        mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), new Rect());
+        assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey());
+        assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+
+        // Drag second bubble, bubble should collapse
+        mBubbleController.startBubbleDrag(mBubbleEntry2.getKey());
+        assertThat(mBubbleController.getLayerView().isExpanded()).isFalse();
+
+        // Stop dragging, first bubble should be expanded
+        mBubbleController.stopBubbleDrag(BubbleBarLocation.LEFT);
+        assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey());
+        assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+    }
+
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void dismissBubbleBarBubble_selected_selectsAndExpandsNext() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        // Add 2 bubbles
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow2);
+        mBubbleController.updateBubble(mBubbleEntry);
+        mBubbleController.updateBubble(mBubbleEntry2);
+
+        // Select first bubble
+        mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), new Rect());
+        // Drag first bubble to dismiss
+        mBubbleController.startBubbleDrag(mBubbleEntry.getKey());
+        mBubbleController.dragBubbleToDismiss(mBubbleEntry.getKey());
+        // Second bubble is selected and expanded
+        assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry2.getKey());
+        assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+    }
+
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void dismissBubbleBarBubble_unselected_selectionDoesNotChange() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        // Add 2 bubbles
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow2);
+        mBubbleController.updateBubble(mBubbleEntry);
+        mBubbleController.updateBubble(mBubbleEntry2);
+
+        // Select first bubble
+        mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), new Rect());
+        // Drag second bubble to dismiss
+        mBubbleController.startBubbleDrag(mBubbleEntry2.getKey());
+        mBubbleController.dragBubbleToDismiss(mBubbleEntry2.getKey());
+        // First bubble remains selected and expanded
+        assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey());
+        assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+    }
+
     @DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
     @Test
     public void doesNotRegisterSensitiveStateListener() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 9dcd946..8eef930 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,8 +15,6 @@
  */
 package com.android.systemui;
 
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
-
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -99,8 +97,22 @@
             .setProvideMainThread(true)
             .build();
 
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+    @ClassRule
+    public static final SetFlagsRule.ClassRule mSetFlagsClassRule =
+            new SetFlagsRule.ClassRule(
+                    android.app.Flags.class,
+                    android.hardware.biometrics.Flags.class,
+                    android.multiuser.Flags.class,
+                    android.net.platform.flags.Flags.class,
+                    android.os.Flags.class,
+                    android.service.controls.flags.Flags.class,
+                    com.android.internal.telephony.flags.Flags.class,
+                    com.android.server.notification.Flags.class,
+                    com.android.systemui.Flags.class);
+
+    // TODO(b/339471826): Fix Robolectric to execute the @ClassRule correctly
+    @Rule public final SetFlagsRule mSetFlagsRule =
+            isRobolectricTest() ? new SetFlagsRule() : mSetFlagsClassRule.createSetFlagsRule();
 
     @Rule(order = 10)
     public final SceneContainerRule mSceneContainerRule = new SceneContainerRule();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
index 0975687..e37bdc1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
@@ -1,8 +1,6 @@
 package com.android.systemui.biometrics.data.repository
 
-import android.hardware.biometrics.Flags
 import android.hardware.biometrics.PromptInfo
-import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.shared.model.PromptKind
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -22,15 +20,12 @@
     private var _challenge = MutableStateFlow<Long?>(null)
     override val challenge = _challenge.asStateFlow()
 
-    private val _kind = MutableStateFlow<PromptKind>(PromptKind.Biometric())
-    override val kind = _kind.asStateFlow()
+    private val _promptKind = MutableStateFlow<PromptKind>(PromptKind.None)
+    override val promptKind = _promptKind.asStateFlow()
 
     private val _isConfirmationRequired = MutableStateFlow(false)
     override val isConfirmationRequired = _isConfirmationRequired.asStateFlow()
 
-    private val _showBpWithoutIconForCredential = MutableStateFlow(false)
-    override val showBpWithoutIconForCredential = _showBpWithoutIconForCredential.asStateFlow()
-
     private val _opPackageName: MutableStateFlow<String?> = MutableStateFlow(null)
     override val opPackageName = _opPackageName.asStateFlow()
 
@@ -61,7 +56,7 @@
         _promptInfo.value = promptInfo
         _userId.value = userId
         _challenge.value = gatekeeperChallenge
-        _kind.value = kind
+        _promptKind.value = kind
         _isConfirmationRequired.value = promptInfo.isConfirmationRequested || forceConfirmation
         _opPackageName.value = opPackageName
     }
@@ -70,22 +65,11 @@
         _promptInfo.value = null
         _userId.value = null
         _challenge.value = null
-        _kind.value = PromptKind.Biometric()
+        _promptKind.value = PromptKind.None
+        _opPackageName.value = null
         _isConfirmationRequired.value = false
     }
 
-    override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
-        val hasCredentialViewShown = kind.value !is PromptKind.Biometric
-        val showBpForCredential =
-            Flags.customBiometricPrompt() &&
-                com.android.systemui.Flags.constraintBp() &&
-                !Utils.isBiometricAllowed(promptInfo) &&
-                Utils.isDeviceCredentialAllowed(promptInfo) &&
-                promptInfo.contentView != null &&
-                !promptInfo.isContentViewMoreOptionsButtonUsed
-        _showBpWithoutIconForCredential.value = showBpForCredential && !hasCredentialViewShown
-    }
-
     fun setIsShowing(showing: Boolean) {
         _isShowing.value = showing
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
index 2ae6f542..4999a5a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.biometrics.ui.viewmodel
 
 import android.content.applicationContext
+import com.android.app.activityTaskManager
+import com.android.launcher3.icons.IconProvider
 import com.android.systemui.biometrics.domain.interactor.biometricStatusInteractor
 import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor
@@ -32,6 +34,8 @@
         context = applicationContext,
         udfpsOverlayInteractor = udfpsOverlayInteractor,
         biometricStatusInteractor = biometricStatusInteractor,
-        udfpsUtils = udfpsUtils
+        udfpsUtils = udfpsUtils,
+        iconProvider = IconProvider(applicationContext),
+        activityTaskManager = activityTaskManager,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt
index d3ceb15..f5d02f3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt
@@ -38,4 +38,8 @@
                 )
             )
     }
+
+    fun setBaseUserRestriction() {
+        _restrictionPolicy.value = PolicyRestriction.Restricted(RestrictedLockUtils.EnforcedAdmin())
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 38f2a56..3401cc4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -27,6 +27,9 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.model.sysUiState
+import com.android.systemui.settings.displayTracker
 
 val Kosmos.shortcutHelperRepository by
     Kosmos.Fixture { ShortcutHelperRepository(fakeCommandQueue, broadcastDispatcher) }
@@ -42,7 +45,9 @@
     }
 
 val Kosmos.shortcutHelperInteractor by
-    Kosmos.Fixture { ShortcutHelperInteractor(shortcutHelperRepository) }
+    Kosmos.Fixture {
+        ShortcutHelperInteractor(displayTracker, testScope, sysUiState, shortcutHelperRepository)
+    }
 
 val Kosmos.shortcutHelperViewModel by
     Kosmos.Fixture { ShortcutHelperViewModel(testDispatcher, shortcutHelperInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
index 90a93f4..a6b40df 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
 import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
 import com.android.systemui.keyguard.ui.viewmodel.keyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardSmartspaceViewModel
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.util.mockito.mock
@@ -37,6 +38,7 @@
             context = applicationContext,
             smartspaceViewModel = keyguardSmartspaceViewModel,
             blueprintInteractor = { keyguardBlueprintInteractor },
+            rootViewModel = keyguardRootViewModel,
         )
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
index 408157b..3e69e87 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
@@ -17,7 +17,10 @@
 package com.android.systemui.keyguard.data.repository
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 
 var Kosmos.keyguardTransitionRepository: KeyguardTransitionRepository by
     Kosmos.Fixture { fakeKeyguardTransitionRepository }
 var Kosmos.fakeKeyguardTransitionRepository by Kosmos.Fixture { FakeKeyguardTransitionRepository() }
+var Kosmos.realKeyguardTransitionRepository: KeyguardTransitionRepository by
+    Kosmos.Fixture { KeyguardTransitionRepositoryImpl(testDispatcher) }
diff --git a/core/java/android/app/StatusBarManager.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt
similarity index 61%
copy from core/java/android/app/StatusBarManager.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt
index 687678c..7d0e8b1 100644
--- a/core/java/android/app/StatusBarManager.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2024, The Android Open Source Project
+/*
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.app;
+package com.android.systemui.keyguard.data.repository
 
-parcelable StatusBarManager.DisableInfo;
\ No newline at end of file
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.lockscreenSceneTransitionRepository by
+    Kosmos.Fixture { LockscreenSceneTransitionRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
index 2c6d44f..03e5a90 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 @ExperimentalCoroutinesApi
@@ -29,5 +30,6 @@
             transitionInteractor = keyguardTransitionInteractor,
             dismissInteractor = keyguardDismissInteractor,
             applicationScope = testScope.backgroundScope,
+            sceneInteractor = sceneInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
index 6cc1e8e..c90642d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 
 val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by
     Kosmos.Fixture {
@@ -32,5 +33,6 @@
             fromAodTransitionInteractor = { fromAodTransitionInteractor },
             fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor },
             fromDozingTransitionInteractor = { fromDozingTransitionInteractor },
+            sceneInteractor = { sceneInteractor }
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
index 29167d6..b38acc8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor
 
 val Kosmos.windowManagerLockscreenVisibilityInteractor by
@@ -29,5 +30,6 @@
             fromBouncerInteractor = fromPrimaryBouncerTransitionInteractor,
             fromAlternateBouncerInteractor = fromAlternateBouncerTransitionInteractor,
             notificationLaunchAnimationInteractor = notificationLaunchAnimationInteractor,
+            sceneInteractor = sceneInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
new file mode 100644
index 0000000..3c1f7b1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor.scenetransition
+
+import com.android.systemui.keyguard.data.repository.lockscreenSceneTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+
+var Kosmos.lockscreenSceneTransitionInteractor by
+    Kosmos.Fixture {
+        LockscreenSceneTransitionInteractor(
+            transitionInteractor = keyguardTransitionInteractor,
+            applicationScope = applicationCoroutineScope,
+            sceneInteractor = sceneInteractor,
+            repository = lockscreenSceneTransitionRepository,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
index 460913f..b8fcec6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
 
 val Kosmos.aodToLockscreenTransitionViewModel by Fixture {
     AodToLockscreenTransitionViewModel(
-        deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
         shadeInteractor = shadeInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt
new file mode 100644
index 0000000..8ad6087
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qrcodescanner
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.qrCodeScannerController by Kosmos.Fixture { mock<QRCodeScannerController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeIQSTileService.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeIQSTileService.kt
index cff5980..744942c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeIQSTileService.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeIQSTileService.kt
@@ -16,11 +16,11 @@
 
 package com.android.systemui.qs.external
 
-import android.os.Binder
 import android.os.IBinder
+import android.os.IInterface
 import android.service.quicksettings.IQSTileService
 
-class FakeIQSTileService : IQSTileService {
+class FakeIQSTileService : IQSTileService.Stub() {
 
     var isTileAdded: Boolean = false
         private set
@@ -31,9 +31,11 @@
         get() = mutableClicks
 
     private val mutableClicks: MutableList<IBinder?> = mutableListOf()
-    private val binder = Binder()
+    override fun queryLocalInterface(descriptor: String): IInterface {
+        return this
+    }
 
-    override fun asBinder(): IBinder = binder
+    override fun asBinder(): IBinder = this
 
     override fun onTileAdded() {
         isTileAdded = true
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerKosmos.kt
new file mode 100644
index 0000000..a0fc76b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerKosmos.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.external
+
+import android.app.activityManager
+import android.content.applicationContext
+import android.os.fakeExecutorHandler
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.packageManagerAdapterFacade
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by
+    Kosmos.Fixture {
+        TileLifecycleManager.Factory { intent, userHandle ->
+            TileLifecycleManager(
+                fakeExecutorHandler,
+                applicationContext,
+                tileServices,
+                packageManagerAdapterFacade.packageManagerAdapter,
+                broadcastDispatcher,
+                intent,
+                userHandle,
+                activityManager,
+                mock(),
+                fakeExecutor,
+            )
+        }
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileServicesKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileServicesKosmos.kt
new file mode 100644
index 0000000..3f129da
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileServicesKosmos.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.external
+
+import android.content.applicationContext
+import android.os.fakeExecutorHandler
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.pipeline.data.repository.customTileAddedRepository
+import com.android.systemui.qs.pipeline.domain.interactor.panelInteractor
+import com.android.systemui.settings.userTracker
+import com.android.systemui.statusbar.commandQueue
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+val Kosmos.tileServices: TileServices by
+    Kosmos.Fixture {
+        val qsHost: QSHost = mock { whenever(context).thenReturn(applicationContext) }
+        TileServices(
+            qsHost,
+            { fakeExecutorHandler },
+            broadcastDispatcher,
+            userTracker,
+            keyguardStateController,
+            commandQueue,
+            mock<StatusBarIconController>(),
+            panelInteractor,
+            tileLifecycleManagerFactory,
+            customTileAddedRepository,
+            fakeExecutor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TilesExternalKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TilesExternalKosmos.kt
index 36c2c2b..9a6730e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TilesExternalKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TilesExternalKosmos.kt
@@ -18,13 +18,9 @@
 
 import android.content.ComponentName
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
 
 var Kosmos.componentName: ComponentName by Kosmos.Fixture()
 
-/** Returns mocks */
-var Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by Kosmos.Fixture { mock {} }
-
 val Kosmos.iQSTileService: FakeIQSTileService by Kosmos.Fixture { FakeIQSTileService() }
 val Kosmos.tileServiceManagerFacade: FakeTileServiceManagerFacade by
     Kosmos.Fixture { FakeTileServiceManagerFacade(iQSTileService) }
@@ -34,4 +30,3 @@
 
 val Kosmos.tileServicesFacade: FakeTileServicesFacade by
     Kosmos.Fixture { (FakeTileServicesFacade(tileServiceManager)) }
-val Kosmos.tileServices: TileServices by Kosmos.Fixture { tileServicesFacade.tileServices }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorKosmos.kt
new file mode 100644
index 0000000..d10780b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.shade.shadeController
+
+val Kosmos.panelInteractor by Kosmos.Fixture { PanelInteractorImpl(shadeController) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
index c4bf8ff..f50443e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
@@ -31,7 +31,11 @@
 
     private val mutableInputs = mutableListOf<Input>()
 
-    override fun handle(expandable: Expandable?, intent: Intent) {
+    override fun handle(
+        expandable: Expandable?,
+        intent: Intent,
+        handleDismissShadeShowOverLockScreenWhenLocked: Boolean
+    ) {
         mutableInputs.add(Input.Intent(expandable, intent))
     }
 
diff --git a/core/java/android/app/StatusBarManager.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
similarity index 62%
copy from core/java/android/app/StatusBarManager.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
index 687678c..ccfb609 100644
--- a/core/java/android/app/StatusBarManager.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2024, The Android Open Source Project
+/*
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
-package android.app;
+package com.android.systemui.qs.tiles.base.actions
 
-parcelable StatusBarManager.DisableInfo;
\ No newline at end of file
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
diff --git a/core/java/android/app/StatusBarManager.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt
similarity index 60%
copy from core/java/android/app/StatusBarManager.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt
index 687678c..146c1ad 100644
--- a/core/java/android/app/StatusBarManager.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2024, The Android Open Source Project
+/*
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.app;
+package com.android.systemui.qs.tiles.base.analytics
 
-parcelable StatusBarManager.DisableInfo;
\ No newline at end of file
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.qsTileAnalytics by Kosmos.Fixture { mock<QSTileAnalytics>() }
diff --git a/core/java/android/app/StatusBarManager.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt
similarity index 62%
copy from core/java/android/app/StatusBarManager.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt
index 687678c..9ad49f0 100644
--- a/core/java/android/app/StatusBarManager.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2024, The Android Open Source Project
+/*
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
-package android.app;
+package com.android.systemui.qs.tiles.base.interactor
 
-parcelable StatusBarManager.DisableInfo;
\ No newline at end of file
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeDisabledByPolicyInteractor by Kosmos.Fixture { FakeDisabledByPolicyInteractor() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
index 7b9992d..42437d5a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.plugins.activityStarter
 import com.android.systemui.qs.external.FakeCustomTileStatePersister
 import com.android.systemui.qs.external.tileServices
+import com.android.systemui.qs.external.tileServicesFacade
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.logging.QSTileLogger
@@ -86,7 +87,7 @@
             customTileInteractor,
             userRepository,
             qsTileLogger,
-            tileServices,
+            tileServicesFacade.tileServices,
             testScope.backgroundScope,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
index 634d121..fa8d363 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles.impl.custom.data.repository
 
 import android.content.ComponentName
+import android.content.pm.PackageInfo
 import android.content.pm.ServiceInfo
 import android.os.Bundle
 import com.android.systemui.qs.external.PackageManagerAdapter
@@ -24,6 +25,7 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
+import org.mockito.ArgumentMatchers.anyInt
 
 /**
  * Facade for [PackageManagerAdapter] to provide a fake-like behaviour. You can create this class
@@ -45,19 +47,33 @@
 
     init {
         whenever(packageManagerAdapter.getServiceInfo(eq(componentName), any())).thenAnswer {
-            ServiceInfo().apply {
-                metaData =
-                    Bundle().apply {
-                        putBoolean(
-                            android.service.quicksettings.TileService.META_DATA_TOGGLEABLE_TILE,
-                            isToggleable
-                        )
-                        putBoolean(
-                            android.service.quicksettings.TileService.META_DATA_ACTIVE_TILE,
-                            isActive
-                        )
-                    }
-            }
+            createServiceInfo()
+        }
+        whenever(
+                packageManagerAdapter.getPackageInfoAsUser(
+                    eq(componentName.packageName),
+                    anyInt(),
+                    anyInt()
+                )
+            )
+            .thenAnswer { PackageInfo().apply { packageName = componentName.packageName } }
+        whenever(packageManagerAdapter.getServiceInfo(eq(componentName), anyInt(), anyInt()))
+            .thenAnswer { createServiceInfo() }
+    }
+
+    private fun createServiceInfo(): ServiceInfo {
+        return ServiceInfo().apply {
+            metaData =
+                Bundle().apply {
+                    putBoolean(
+                        android.service.quicksettings.TileService.META_DATA_TOGGLEABLE_TILE,
+                        isToggleable
+                    )
+                    putBoolean(
+                        android.service.quicksettings.TileService.META_DATA_ACTIVE_TILE,
+                        isActive
+                    )
+                }
         }
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
new file mode 100644
index 0000000..dcfcce7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr
+
+import android.content.res.mainResources
+import com.android.systemui.classifier.fakeFalsingManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule
+import com.android.systemui.qrcodescanner.qrCodeScannerController
+import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.analytics.qsTileAnalytics
+import com.android.systemui.qs.tiles.base.interactor.fakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
+import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor
+import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.qr.ui.QRCodeScannerTileMapper
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.time.systemClock
+
+val Kosmos.qsQRCodeScannerTileConfig by
+    Kosmos.Fixture { QRCodeScannerModule.provideQRCodeScannerTileConfig(qsEventLogger) }
+
+val Kosmos.qrCodeScannerTileDataInteractor by
+    Kosmos.Fixture {
+        QRCodeScannerTileDataInteractor(
+            backgroundCoroutineContext,
+            applicationCoroutineScope,
+            qrCodeScannerController
+        )
+    }
+
+val Kosmos.qrCodeScannerTileUserActionInteractor by
+    Kosmos.Fixture { QRCodeScannerTileUserActionInteractor(qsTileIntentUserInputHandler) }
+
+val Kosmos.qrCodeScannerTileMapper by
+    Kosmos.Fixture { QRCodeScannerTileMapper(mainResources, mainResources.newTheme()) }
+
+val Kosmos.qsQRCodeScannerViewModel by
+    Kosmos.Fixture {
+        QSTileViewModelImpl(
+            qsQRCodeScannerTileConfig,
+            { qrCodeScannerTileUserActionInteractor },
+            { qrCodeScannerTileDataInteractor },
+            { qrCodeScannerTileMapper },
+            fakeDisabledByPolicyInteractor,
+            fakeUserRepository,
+            fakeFalsingManager,
+            qsTileAnalytics,
+            qsTileLogger,
+            systemClock,
+            testDispatcher,
+            testScope.backgroundScope,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
new file mode 100644
index 0000000..641a757
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.data.repository
+
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.model.Scenes
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+
+private val mutableTransitionState =
+    MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(Scenes.Lockscreen))
+
+fun Kosmos.setSceneTransition(
+    transition: ObservableTransitionState,
+    scope: TestScope = testScope,
+    repository: SceneContainerRepository = sceneContainerRepository
+) {
+    repository.setTransitionState(mutableTransitionState)
+    mutableTransitionState.value = transition
+    scope.runCurrent()
+}
+
+fun Transition(
+    from: SceneKey,
+    to: SceneKey,
+    currentScene: Flow<SceneKey> = flowOf(to),
+    progress: Flow<Float> = flowOf(0f),
+    isInitiatedByUserInput: Boolean = false,
+    isUserInputOngoing: Flow<Boolean> = flowOf(false),
+): ObservableTransitionState.Transition {
+    return ObservableTransitionState.Transition(
+        fromScene = from,
+        toScene = to,
+        currentScene = currentScene,
+        progress = progress,
+        isInitiatedByUserInput = isInitiatedByUserInput,
+        isUserInputOngoing = isUserInputOngoing
+    )
+}
+
+fun Idle(currentScene: SceneKey): ObservableTransitionState.Idle {
+    return ObservableTransitionState.Idle(currentScene)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 2bd584e..562ac0c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -27,6 +27,8 @@
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.logging.InstanceId;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SbnBuilder;
@@ -36,6 +38,7 @@
 import kotlin.Unit;
 
 import java.util.ArrayList;
+import java.util.function.Consumer;
 
 /**
  * Combined builder for constructing a NotificationEntry and its associated StatusBarNotification
@@ -73,6 +76,20 @@
         mCreationTime = source.getCreationTime();
     }
 
+    /** Allows the caller to sub-build the ranking */
+    @NonNull
+    public NotificationEntryBuilder updateRanking(@NonNull Consumer<RankingBuilder> rankingUpdater) {
+        rankingUpdater.accept(mRankingBuilder);
+        return this;
+    }
+
+    /** Allows the caller to sub-build the SBN */
+    @NonNull
+    public NotificationEntryBuilder updateSbn(@NonNull Consumer<SbnBuilder> sbnUpdater) {
+        sbnUpdater.accept(mSbnBuilder);
+        return this;
+    }
+
     /** Update an the parent on an existing entry */
     public static void setNewParent(NotificationEntry entry, GroupEntry parent) {
         entry.setParent(parent);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index cce038f..8229575 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -23,6 +23,7 @@
 import com.android.settingslib.mobile.MobileMappings
 import com.android.settingslib.mobile.TelephonyIcons
 import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
@@ -93,6 +94,8 @@
     private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON)
     override val defaultMobileIconGroup = _defaultMobileIconGroup
 
+    override val deviceServiceState = MutableStateFlow<ServiceStateModel?>(null)
+
     override val isAnySimSecure = MutableStateFlow(false)
     override fun getIsAnySimSecure(): Boolean = isAnySimSecure.value
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index de6c87c2..3a4bf8e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -81,6 +81,8 @@
 
     override val isForceHidden = MutableStateFlow(false)
 
+    override val isDeviceInEmergencyCallsOnlyMode = MutableStateFlow(false)
+
     /** Always returns a new fake interactor */
     override fun getMobileConnectionInteractorForSubId(subId: Int): FakeMobileIconInteractor {
         return FakeMobileIconInteractor(tableLogBuffer).also {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/MockExecutorHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/MockExecutorHandler.kt
index 184d4b5..92f9248 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/MockExecutorHandler.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/MockExecutorHandler.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.util.concurrency
 
 import android.os.Handler
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.Executor
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyLong
@@ -29,9 +31,14 @@
  * Wrap an [Executor] in a mock [Handler] that execute when [Handler.post] is called, and throws an
  * exception otherwise. This is useful when a class requires a Handler only because Handlers are
  * used by ContentObserver, and no other methods are used.
+ *
+ * If the [executor] is a [DelayableExecutor], it also supports:
+ * * [Handler.postDelayed] with a Runnable parameter
+ * * [Handler.postAtTime] with a RunnableParameter
  */
 fun mockExecutorHandler(executor: Executor): Handler {
     val handlerMock = Mockito.mock(Handler::class.java, RuntimeExceptionAnswer())
+    val cancellations = ConcurrentHashMap<Runnable, MutableList<Cancellation>>()
     doAnswer { invocation: InvocationOnMock ->
             executor.execute(invocation.getArgument(0))
             true
@@ -42,7 +49,19 @@
         doAnswer { invocation: InvocationOnMock ->
                 val runnable = invocation.getArgument<Runnable>(0)
                 val uptimeMillis = invocation.getArgument<Long>(1)
-                executor.executeAtTime(runnable, uptimeMillis)
+                val token = Any()
+                val canceller =
+                    executor.executeAtTime(
+                        {
+                            cancellations.get(runnable)?.removeIf { it.token == token }
+                            cancellations.remove(runnable, emptyList())
+                            runnable.run()
+                        },
+                        uptimeMillis
+                    )
+                cancellations
+                    .getOrPut(runnable) { CopyOnWriteArrayList() }
+                    .add(Cancellation(token, canceller))
                 true
             }
             .`when`(handlerMock)
@@ -50,15 +69,36 @@
         doAnswer { invocation: InvocationOnMock ->
                 val runnable = invocation.getArgument<Runnable>(0)
                 val delayInMillis = invocation.getArgument<Long>(1)
-                executor.executeDelayed(runnable, delayInMillis)
+                val token = Any()
+                val canceller =
+                    executor.executeDelayed(
+                        {
+                            cancellations.get(runnable)?.removeIf { it.token == token }
+                            cancellations.remove(runnable, emptyList())
+                            runnable.run()
+                        },
+                        delayInMillis
+                    )
+                cancellations
+                    .getOrPut(runnable) { CopyOnWriteArrayList() }
+                    .add(Cancellation(token, canceller))
                 true
             }
             .`when`(handlerMock)
             .postDelayed(any(), anyLong())
+        doAnswer { invocation: InvocationOnMock ->
+                val runnable = invocation.getArgument<Runnable>(0)
+                cancellations.remove(runnable)?.forEach(Runnable::run)
+                Unit
+            }
+            .`when`(handlerMock)
+            .removeCallbacks(any())
     }
     return handlerMock
 }
 
+private class Cancellation(val token: Any, canceller: Runnable) : Runnable by canceller
+
 private class RuntimeExceptionAnswer : Answer<Any> {
     override fun answer(invocation: InvocationOnMock): Any {
         throw RuntimeException(invocation.method.name + " is not stubbed")
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
index 59bbff1..1b58582 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
@@ -18,8 +18,6 @@
 
 import android.content.packageManager
 import android.content.pm.ApplicationInfo
-import android.os.Handler
-import android.os.looper
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.mediaOutputDialogManager
@@ -32,6 +30,7 @@
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaControllerInteractor
 
 val Kosmos.localMediaRepository by Kosmos.Fixture { FakeLocalMediaRepository() }
 val Kosmos.localMediaRepositoryFactory by
@@ -53,7 +52,7 @@
             testScope.backgroundScope,
             testScope.testScheduler,
             mediaControllerRepository,
-            Handler(looper),
+            mediaControllerInteractor,
         )
     }
 
@@ -61,7 +60,7 @@
     Kosmos.Fixture {
         MediaDeviceSessionInteractor(
             testScope.testScheduler,
-            Handler(looper),
+            mediaControllerInteractor,
             mediaControllerRepository,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
index 617fc52..6b27079 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.volume.data.repository
 
 import android.media.AudioDeviceInfo
+import android.media.AudioManager
 import com.android.settingslib.volume.data.repository.AudioRepository
 import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.settingslib.volume.shared.model.AudioStreamModel
@@ -29,10 +30,10 @@
 
 class FakeAudioRepository : AudioRepository {
 
-    private val mutableMode = MutableStateFlow(0)
+    private val mutableMode = MutableStateFlow(AudioManager.MODE_NORMAL)
     override val mode: StateFlow<Int> = mutableMode.asStateFlow()
 
-    private val mutableRingerMode = MutableStateFlow(RingerMode(0))
+    private val mutableRingerMode = MutableStateFlow(RingerMode(AudioManager.RINGER_MODE_NORMAL))
     override val ringerMode: StateFlow<RingerMode> = mutableRingerMode.asStateFlow()
 
     private val mutableCommunicationDevice = MutableStateFlow<AudioDeviceInfo?>(null)
@@ -53,7 +54,7 @@
                     audioStream = audioStream,
                     volume = 0,
                     minVolume = 0,
-                    maxVolume = 0,
+                    maxVolume = 10,
                     isAffectedByRingerMode = false,
                     isMuted = false,
                 )
@@ -67,8 +68,14 @@
         getAudioStreamModelState(audioStream).update { it.copy(volume = volume) }
     }
 
-    override suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) {
-        getAudioStreamModelState(audioStream).update { it.copy(isMuted = isMuted) }
+    override suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean {
+        val modelState = getAudioStreamModelState(audioStream)
+        return if (modelState.value.isMuted == isMuted) {
+            false
+        } else {
+            modelState.update { it.copy(isMuted = isMuted) }
+            true
+        }
     }
 
     override suspend fun getLastAudibleVolume(audioStream: AudioStream): Int =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt
new file mode 100644
index 0000000..f03ec01
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.media.session.MediaController
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+class FakeMediaControllerInteractor : MediaControllerInteractor {
+
+    private val stateChanges = MutableSharedFlow<MediaControllerChangeModel>(replay = 1)
+
+    override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> =
+        stateChanges
+
+    fun updateState(change: MediaControllerChangeModel) {
+        stateChanges.tryEmit(change)
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt
new file mode 100644
index 0000000..652b3ea
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.os.Handler
+import android.os.looper
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.mediaControllerInteractor: MediaControllerInteractor by
+    Kosmos.Fixture { MediaControllerInteractorImpl(Handler(looper)) }
diff --git a/proto/src/am_capabilities.proto b/proto/src/am_capabilities.proto
index fc9f7a45..c2b3ac2 100644
--- a/proto/src/am_capabilities.proto
+++ b/proto/src/am_capabilities.proto
@@ -15,8 +15,16 @@
   string name  = 1;
 }
 
+message VMInfo {
+  // The value of the "java.vm.name" system property
+  string name = 1;
+  // The value of the "java.vm.version" system property
+  string version = 2;
+}
+
 message Capabilities {
   repeated Capability values = 1;
   repeated VMCapability vm_capabilities = 2;
   repeated FrameworkCapability framework_capabilities = 3;
+  VMInfo vm_info = 4;
 }
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index bc608c5..95cbb6b 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -221,7 +221,9 @@
     data: [
         ":framework-minus-apex.ravenwood.stats",
         ":framework-minus-apex.ravenwood.apis",
+        ":framework-minus-apex.ravenwood.keep_all",
         ":services.core.ravenwood.stats",
         ":services.core.ravenwood.apis",
+        ":services.core.ravenwood.keep_all",
     ],
 }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java
new file mode 100644
index 0000000..4992c4b
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.ravenwood;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.IntentSender.SendIntentException;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A subclass of Context with all the abstract methods replaced with concrete methods.
+ *
+ * <p>In order to make sure it implements all the abstract methods, we intentionally keep it
+ * non-abstract.
+ */
+public class RavenwoodBaseContext extends Context {
+    RavenwoodBaseContext() {
+        // Only usable by ravenwood.
+    }
+
+    private static RuntimeException notSupported() {
+        return new RuntimeException("This Context API is not yet supported under"
+                + " the Ravenwood deviceless testing environment. Contact g/ravenwood");
+    }
+
+    @Override
+    public AssetManager getAssets() {
+        throw notSupported();
+    }
+
+    @Override
+    public Resources getResources() {
+        throw notSupported();
+    }
+
+    @Override
+    public PackageManager getPackageManager() {
+        throw notSupported();
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        throw notSupported();
+    }
+
+    @Override
+    public Looper getMainLooper() {
+        throw notSupported();
+    }
+
+    @Override
+    public Context getApplicationContext() {
+        throw notSupported();
+    }
+
+    @Override
+    public void setTheme(int resid) {
+        throw notSupported();
+    }
+
+    @Override
+    public Theme getTheme() {
+        throw notSupported();
+    }
+
+    @Override
+    public ClassLoader getClassLoader() {
+        throw notSupported();
+    }
+
+    @Override
+    public String getPackageName() {
+        throw notSupported();
+    }
+
+    @Override
+    public String getBasePackageName() {
+        throw notSupported();
+    }
+
+    @Override
+    public ApplicationInfo getApplicationInfo() {
+        throw notSupported();
+    }
+
+    @Override
+    public String getPackageResourcePath() {
+        throw notSupported();
+    }
+
+    @Override
+    public String getPackageCodePath() {
+        throw notSupported();
+    }
+
+    @Override
+    public SharedPreferences getSharedPreferences(String name, int mode) {
+        throw notSupported();
+    }
+
+    @Override
+    public SharedPreferences getSharedPreferences(File file, int mode) {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean deleteSharedPreferences(String name) {
+        throw notSupported();
+    }
+
+    @Override
+    public void reloadSharedPreferences() {
+        throw notSupported();
+    }
+
+    @Override
+    public FileInputStream openFileInput(String name) throws FileNotFoundException {
+        throw notSupported();
+    }
+
+    @Override
+    public FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean deleteFile(String name) {
+        throw notSupported();
+    }
+
+    @Override
+    public File getFileStreamPath(String name) {
+        throw notSupported();
+    }
+
+    @Override
+    public File getSharedPreferencesPath(String name) {
+        throw notSupported();
+    }
+
+    @Override
+    public File getDataDir() {
+        throw notSupported();
+    }
+
+    @Override
+    public File getFilesDir() {
+        throw notSupported();
+    }
+
+    @Override
+    public File getNoBackupFilesDir() {
+        throw notSupported();
+    }
+
+    @Override
+    public File getExternalFilesDir(String type) {
+        throw notSupported();
+    }
+
+    @Override
+    public File[] getExternalFilesDirs(String type) {
+        throw notSupported();
+    }
+
+    @Override
+    public File getObbDir() {
+        throw notSupported();
+    }
+
+    @Override
+    public File[] getObbDirs() {
+        throw notSupported();
+    }
+
+    @Override
+    public File getCacheDir() {
+        throw notSupported();
+    }
+
+    @Override
+    public File getCodeCacheDir() {
+        throw notSupported();
+    }
+
+    @Override
+    public File getExternalCacheDir() {
+        throw notSupported();
+    }
+
+    @Override
+    public File getPreloadsFileCache() {
+        throw notSupported();
+    }
+
+    @Override
+    public File[] getExternalCacheDirs() {
+        throw notSupported();
+    }
+
+    @Override
+    public File[] getExternalMediaDirs() {
+        throw notSupported();
+    }
+
+    @Override
+    public String[] fileList() {
+        throw notSupported();
+    }
+
+    @Override
+    public File getDir(String name, int mode) {
+        throw notSupported();
+    }
+
+    @Override
+    public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
+        throw notSupported();
+    }
+
+    @Override
+    public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
+            DatabaseErrorHandler errorHandler) {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean moveDatabaseFrom(Context sourceContext, String name) {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean deleteDatabase(String name) {
+        throw notSupported();
+    }
+
+    @Override
+    public File getDatabasePath(String name) {
+        throw notSupported();
+    }
+
+    @Override
+    public String[] databaseList() {
+        throw notSupported();
+    }
+
+    @Override
+    public Drawable getWallpaper() {
+        throw notSupported();
+    }
+
+    @Override
+    public Drawable peekWallpaper() {
+        throw notSupported();
+    }
+
+    @Override
+    public int getWallpaperDesiredMinimumWidth() {
+        throw notSupported();
+    }
+
+    @Override
+    public int getWallpaperDesiredMinimumHeight() {
+        throw notSupported();
+    }
+
+    @Override
+    public void setWallpaper(Bitmap bitmap) throws IOException {
+        throw notSupported();
+    }
+
+    @Override
+    public void setWallpaper(InputStream data) throws IOException {
+        throw notSupported();
+    }
+
+    @Override
+    public void clearWallpaper() throws IOException {
+        throw notSupported();
+    }
+
+    @Override
+    public void startActivity(Intent intent) {
+        throw notSupported();
+    }
+
+    @Override
+    public void startActivity(Intent intent, Bundle options) {
+        throw notSupported();
+    }
+
+    @Override
+    public void startActivities(Intent[] intents) {
+        throw notSupported();
+    }
+
+    @Override
+    public void startActivities(Intent[] intents, Bundle options) {
+        throw notSupported();
+    }
+
+    @Override
+    public void startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask,
+            int flagsValues, int extraFlags) throws SendIntentException {
+        throw notSupported();
+    }
+
+    @Override
+    public void startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask,
+            int flagsValues, int extraFlags, Bundle options) throws SendIntentException {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
+            String[] receiverPermissions) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission,
+            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+            String initialData, Bundle initialExtras) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission,
+            int appOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+            String initialData, Bundle initialExtras) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission,
+            Bundle options) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission,
+            int appOp) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
+            int initialCode, String initialData, Bundle initialExtras) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission, int appOp, Bundle options,
+            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+            String initialData, Bundle initialExtras) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendStickyBroadcast(Intent intent) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+        throw notSupported();
+
+    }
+
+    @Override
+    public void removeStickyBroadcast(Intent intent) {
+        throw notSupported();
+
+    }
+
+    @Override
+    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
+        throw notSupported();
+    }
+
+    @Override
+    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+        throw notSupported();
+
+    }
+
+    @Override
+    public void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+            String initialData, Bundle initialExtras) {
+        throw notSupported();
+    }
+
+    @Override
+    public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
+        throw notSupported();
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        throw notSupported();
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+        throw notSupported();
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler) {
+        throw notSupported();
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler, int flags) {
+        throw notSupported();
+    }
+
+    @Override
+    public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+            IntentFilter filter, String broadcastPermission, Handler scheduler) {
+        throw notSupported();
+    }
+
+    @Override
+    public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+            IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
+        throw notSupported();
+    }
+
+    @Override
+    public void unregisterReceiver(BroadcastReceiver receiver) {
+        throw notSupported();
+    }
+
+    @Override
+    public ComponentName startService(Intent service) {
+        throw notSupported();
+    }
+
+    @Override
+    public ComponentName startForegroundService(Intent service) {
+        throw notSupported();
+    }
+
+    @Override
+    public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean stopService(Intent service) {
+        throw notSupported();
+    }
+
+    @Override
+    public ComponentName startServiceAsUser(Intent service, UserHandle user) {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean stopServiceAsUser(Intent service, UserHandle user) {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+        throw notSupported();
+    }
+
+    @Override
+    public void unbindService(ServiceConnection conn) {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean startInstrumentation(ComponentName className, String profileFile,
+            Bundle arguments) {
+        throw notSupported();
+    }
+
+    @Override
+    public Object getSystemService(String name) {
+        throw notSupported();
+    }
+
+    @Override
+    public String getSystemServiceName(Class<?> serviceClass) {
+        throw notSupported();
+    }
+
+    @Override
+    public int checkPermission(String permission, int pid, int uid) {
+        throw notSupported();
+    }
+
+    @Override
+    public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+        throw notSupported();
+    }
+
+    @Override
+    public int checkCallingPermission(String permission) {
+        throw notSupported();
+    }
+
+    @Override
+    public int checkCallingOrSelfPermission(String permission) {
+        throw notSupported();
+    }
+
+    @Override
+    public int checkSelfPermission(String permission) {
+        throw notSupported();
+    }
+
+    @Override
+    public void enforcePermission(String permission, int pid, int uid, String message) {
+        throw notSupported();
+    }
+
+    @Override
+    public void enforceCallingPermission(String permission, String message) {
+        throw notSupported();
+    }
+
+    @Override
+    public void enforceCallingOrSelfPermission(String permission, String message) {
+        throw notSupported();
+    }
+
+    @Override
+    public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
+        throw notSupported();
+    }
+
+    @Override
+    public void revokeUriPermission(Uri uri, int modeFlags) {
+        throw notSupported();
+    }
+
+    @Override
+    public void revokeUriPermission(String toPackage, Uri uri, int modeFlags) {
+        throw notSupported();
+    }
+
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
+        throw notSupported();
+    }
+
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+        throw notSupported();
+    }
+
+    @Override
+    public int checkCallingUriPermission(Uri uri, int modeFlags) {
+        throw notSupported();
+    }
+
+    @Override
+    public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) {
+        throw notSupported();
+    }
+
+    @Override
+    public int checkUriPermission(Uri uri, String readPermission, String writePermission,
+            int pid, int uid, int modeFlags) {
+        throw notSupported();
+    }
+
+    @Override
+    public void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message) {
+        throw notSupported();
+    }
+
+    @Override
+    public void enforceCallingUriPermission(Uri uri, int modeFlags, String message) {
+        throw notSupported();
+    }
+
+    @Override
+    public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message) {
+        throw notSupported();
+    }
+
+    @Override
+    public void enforceUriPermission(Uri uri, String readPermission, String writePermission,
+            int pid, int uid, int modeFlags, String message) {
+        throw notSupported();
+    }
+
+    @Override
+    public Context createPackageContext(String packageName, int flags)
+            throws NameNotFoundException {
+        throw notSupported();
+    }
+
+    @Override
+    public Context createApplicationContext(ApplicationInfo application, int flags)
+            throws NameNotFoundException {
+        throw notSupported();
+    }
+
+    @Override
+    public Context createContextForSplit(String splitName) throws NameNotFoundException {
+        throw notSupported();
+    }
+
+    @Override
+    public Context createConfigurationContext(Configuration overrideConfiguration) {
+        throw notSupported();
+    }
+
+    @Override
+    public Context createDisplayContext(Display display) {
+        throw notSupported();
+    }
+
+    @Override
+    public Context createDeviceProtectedStorageContext() {
+        throw notSupported();
+    }
+
+    @Override
+    public Context createCredentialProtectedStorageContext() {
+        throw notSupported();
+    }
+
+    @Override
+    public DisplayAdjustments getDisplayAdjustments(int displayId) {
+        throw notSupported();
+    }
+
+    @Override
+    public int getDisplayId() {
+        throw notSupported();
+    }
+
+    @Override
+    public void updateDisplay(int displayId) {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean isDeviceProtectedStorage() {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean isCredentialProtectedStorage() {
+        throw notSupported();
+    }
+
+    @Override
+    public boolean canLoadUnsafeResources() {
+        throw notSupported();
+    }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index 109ef76..1dd5e1d 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -28,7 +28,6 @@
 import android.os.UserHandle;
 import android.ravenwood.example.BlueManager;
 import android.ravenwood.example.RedManager;
-import android.test.mock.MockContext;
 import android.util.ArrayMap;
 import android.util.Singleton;
 
@@ -36,7 +35,7 @@
 import java.util.concurrent.Executor;
 import java.util.function.Supplier;
 
-public class RavenwoodContext extends MockContext {
+public class RavenwoodContext extends RavenwoodBaseContext {
     private final String mPackageName;
     private final HandlerThread mMainThread;
 
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
index 96b7057..69ff262 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
@@ -162,20 +162,23 @@
             android.graphics.Interpolator.class,
             android.graphics.Matrix.class,
             android.graphics.Path.class,
+            android.graphics.Color.class,
+            android.graphics.ColorSpace.class,
     };
 
     /**
-     * @return if a given class has any native method or not.
+     * @return if a given class and its nested classes, if any, have any native method or not.
      */
     private static boolean hasNativeMethod(Class<?> clazz) {
-        for (var method : clazz.getDeclaredMethods()) {
-            if (Modifier.isNative(method.getModifiers())) {
-                return true;
+        for (var nestedClass : clazz.getNestMembers()) {
+            for (var method : nestedClass.getDeclaredMethods()) {
+                if (Modifier.isNative(method.getModifiers())) {
+                    return true;
+                }
             }
         }
         return false;
     }
-
     /**
      * Create a list of classes as comma-separated that require JNI methods to be set up from
      * a given class list, ignoring classes with no native methods.
diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh
index cf58bd2..43b61a4 100755
--- a/ravenwood/scripts/ravenwood-stats-collector.sh
+++ b/ravenwood/scripts/ravenwood-stats-collector.sh
@@ -18,8 +18,14 @@
 set -e
 
 # Output files
-stats=/tmp/ravenwood-stats-all.csv
-apis=/tmp/ravenwood-apis-all.csv
+out_dir=/tmp/ravenwood
+stats=$out_dir/ravenwood-stats-all.csv
+apis=$out_dir/ravenwood-apis-all.csv
+keep_all_dir=$out_dir/ravenwood-keep-all/
+
+rm -fr $out_dir
+mkdir -p $out_dir
+mkdir -p $keep_all_dir
 
 # Where the input files are.
 path=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/ravenwood-stats-checker/x86_64/
@@ -76,3 +82,7 @@
 
 collect_stats $stats
 collect_apis $apis
+
+cp *keep_all.txt $keep_all_dir
+echo "Keep all files created at:"
+find $keep_all_dir -type f
\ No newline at end of file
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index e452299..f3172ae 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -1,5 +1,7 @@
 # Only classes listed here can use the Ravenwood annotations.
 
+com.android.internal.ravenwood.*
+
 com.android.internal.display.BrightnessSynchronizer
 com.android.internal.util.ArrayUtils
 com.android.internal.logging.MetricsLogger
@@ -237,6 +239,8 @@
 android.accounts.Account
 
 android.graphics.Bitmap$Config
+android.graphics.Color
+android.graphics.ColorSpace
 android.graphics.Insets
 android.graphics.Interpolator
 android.graphics.Matrix
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 371c3ac..9d29a05 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -1,59 +1,59 @@
 # Ravenwood "policy" file for framework-minus-apex.
 
 # Keep all AIDL interfaces
-class :aidl stubclass
+class :aidl keepclass
 
 # Keep all feature flag implementations
-class :feature_flags stubclass
+class :feature_flags keepclass
 
 # Keep all sysprops generated code implementations
-class :sysprops stubclass
+class :sysprops keepclass
 
 # Exported to Mainline modules; cannot use annotations
-class com.android.internal.util.FastXmlSerializer stubclass
-class com.android.internal.util.FileRotator stubclass
-class com.android.internal.util.HexDump stubclass
-class com.android.internal.util.IndentingPrintWriter stubclass
-class com.android.internal.util.LocalLog stubclass
-class com.android.internal.util.MessageUtils stubclass
-class com.android.internal.util.TokenBucket stubclass
-class android.os.HandlerExecutor stubclass
-class android.util.BackupUtils stubclass
-class android.util.IndentingPrintWriter stubclass
-class android.util.LocalLog stubclass
-class android.util.Pair stubclass
-class android.util.Rational stubclass
+class com.android.internal.util.FastXmlSerializer keepclass
+class com.android.internal.util.FileRotator keepclass
+class com.android.internal.util.HexDump keepclass
+class com.android.internal.util.IndentingPrintWriter keepclass
+class com.android.internal.util.LocalLog keepclass
+class com.android.internal.util.MessageUtils keepclass
+class com.android.internal.util.TokenBucket keepclass
+class android.os.HandlerExecutor keepclass
+class android.util.BackupUtils keepclass
+class android.util.IndentingPrintWriter keepclass
+class android.util.LocalLog keepclass
+class android.util.Pair keepclass
+class android.util.Rational keepclass
 
 # From modules-utils; cannot use annotations
-class com.android.internal.util.Preconditions stubclass
-class com.android.internal.logging.InstanceId stubclass
-class com.android.internal.logging.InstanceIdSequence stubclass
-class com.android.internal.logging.UiEvent stubclass
-class com.android.internal.logging.UiEventLogger stubclass
+class com.android.internal.util.Preconditions keepclass
+class com.android.internal.logging.InstanceId keepclass
+class com.android.internal.logging.InstanceIdSequence keepclass
+class com.android.internal.logging.UiEvent keepclass
+class com.android.internal.logging.UiEventLogger keepclass
 
 # From modules-utils; cannot use annotations
-class com.android.modules.utils.BinaryXmlPullParser stubclass
-class com.android.modules.utils.BinaryXmlSerializer stubclass
-class com.android.modules.utils.FastDataInput stubclass
-class com.android.modules.utils.FastDataOutput stubclass
-class com.android.modules.utils.ModifiedUtf8 stubclass
-class com.android.modules.utils.TypedXmlPullParser stubclass
-class com.android.modules.utils.TypedXmlSerializer stubclass
+class com.android.modules.utils.BinaryXmlPullParser keepclass
+class com.android.modules.utils.BinaryXmlSerializer keepclass
+class com.android.modules.utils.FastDataInput keepclass
+class com.android.modules.utils.FastDataOutput keepclass
+class com.android.modules.utils.ModifiedUtf8 keepclass
+class com.android.modules.utils.TypedXmlPullParser keepclass
+class com.android.modules.utils.TypedXmlSerializer keepclass
 
 # Uri
-class android.net.Uri stubclass
-class android.net.UriCodec stubclass
+class android.net.Uri keepclass
+class android.net.UriCodec keepclass
 
 # Telephony
-class android.telephony.PinResult stubclass
+class android.telephony.PinResult keepclass
 
 # Just enough to support mocking, no further functionality
-class android.content.BroadcastReceiver stub
-    method <init> ()V stub
-class android.content.Context stub
-    method <init> ()V stub
-    method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; stub
-class android.content.pm.PackageManager stub
-    method <init> ()V stub
-class android.text.ClipboardManager stub
-    method <init> ()V stub
+class android.content.BroadcastReceiver keep
+    method <init> ()V keep
+class android.content.Context keep
+    method <init> ()V keep
+    method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; keep
+class android.content.pm.PackageManager keep
+    method <init> ()V keep
+class android.text.ClipboardManager keep
+    method <init> ()V keep
diff --git a/ravenwood/texts/ravenwood-services-policies.txt b/ravenwood/texts/ravenwood-services-policies.txt
index d8d563e..5cdb4f7 100644
--- a/ravenwood/texts/ravenwood-services-policies.txt
+++ b/ravenwood/texts/ravenwood-services-policies.txt
@@ -1,7 +1,7 @@
 # Ravenwood "policy" file for services.core.
 
 # Keep all AIDL interfaces
-class :aidl stubclass
+class :aidl keepclass
 
 # Keep all feature flag implementations
-class :feature_flags stubclass
+class :feature_flags keepclass
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 82579d8..3f30b0a 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -152,6 +152,16 @@
 }
 
 flag {
+    name: "remove_on_window_infos_changed_handler"
+    namespace: "accessibility"
+    description: "Updates onWindowInfosChanged() to run without posting to a handler."
+    bug: "333834990"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "reset_hover_event_timer_on_action_up"
     namespace: "accessibility"
     description: "Reset the timer for sending hover events on receiving ACTION_UP to guarantee the correct amount of time is available between taps."
@@ -183,3 +193,10 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "enable_color_correction_saturation"
+    namespace: "accessibility"
+    description: "Feature allows users to change color correction saturation for daltonizer."
+    bug: "322829049"
+}
\ No newline at end of file
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 06a0297..71b16c3 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -172,8 +172,7 @@
         OnCrossProfileWidgetProvidersChangeListener {
     private static final String TAG = "AppWidgetServiceImpl";
 
-    private static final boolean DEBUG = false;
-    private static final boolean DEBUG_NULL_PROVIDER_INFO = Build.IS_DEBUGGABLE;
+    private static final boolean DEBUG = Build.IS_DEBUGGABLE;
 
     private static final String OLD_KEYGUARD_HOST_PACKAGE = "android";
     private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
@@ -1573,9 +1572,36 @@
                     Binder.getCallingUid(), callingPackage);
 
             if (widget != null && widget.provider != null && !widget.provider.zombie) {
-                return cloneIfLocalBinder(widget.provider.getInfoLocked(mContext));
+                final AppWidgetProviderInfo info = widget.provider.getInfoLocked(mContext);
+                if (info == null) {
+                    Slog.e(TAG, "getAppWidgetInfo() returns null because"
+                            + " widget.provider.getInfoLocked() returned null."
+                            + " appWidgetId=" + appWidgetId + " userId=" + userId
+                            + " widget=" + widget);
+                    return null;
+                }
+                final AppWidgetProviderInfo ret = cloneIfLocalBinder(info);
+                if (ret == null) {
+                    Slog.e(TAG, "getAppWidgetInfo() returns null because"
+                            + " cloneIfLocalBinder() returned null."
+                            + " appWidgetId=" + appWidgetId + " userId=" + userId
+                            + " widget=" + widget + " appWidgetProviderInfo=" + info);
+                }
+                return ret;
+            } else {
+                if (widget == null) {
+                    Slog.e(TAG, "getAppWidgetInfo() returns null because widget is null."
+                            + " appWidgetId=" + appWidgetId + " userId=" + userId);
+                } else if (widget.provider == null) {
+                    Slog.e(TAG, "getAppWidgetInfo() returns null because widget.provider is null."
+                            + " appWidgetId=" + appWidgetId + " userId=" + userId
+                            + " widget=" + widget);
+                } else {
+                    Slog.e(TAG, "getAppWidgetInfo() returns null because widget.provider is zombie."
+                            + " appWidgetId=" + appWidgetId + " userId=" + userId
+                            + " widget=" + widget);
+                }
             }
-
             return null;
         }
     }
@@ -2960,7 +2986,7 @@
             AppWidgetProviderInfo info = new AppWidgetProviderInfo();
             info.provider = providerId.componentName;
             info.providerInfo = ri.activityInfo;
-            if (DEBUG_NULL_PROVIDER_INFO) {
+            if (DEBUG) {
                 Objects.requireNonNull(ri.activityInfo);
             }
             return info;
@@ -2997,7 +3023,7 @@
             AppWidgetProviderInfo info = new AppWidgetProviderInfo();
             info.provider = providerId.componentName;
             info.providerInfo = activityInfo;
-            if (DEBUG_NULL_PROVIDER_INFO) {
+            if (DEBUG) {
                 Objects.requireNonNull(activityInfo);
             }
 
@@ -3575,7 +3601,7 @@
                             AppWidgetProviderInfo info = new AppWidgetProviderInfo();
                             info.provider = providerId.componentName;
                             info.providerInfo = providerInfo;
-                            if (DEBUG_NULL_PROVIDER_INFO) {
+                            if (DEBUG) {
                                 Objects.requireNonNull(providerInfo);
                             }
 
@@ -3594,7 +3620,7 @@
                             if (info != null) {
                                 info.provider = providerId.componentName;
                                 info.providerInfo = providerInfo;
-                                if (DEBUG_NULL_PROVIDER_INFO) {
+                                if (DEBUG) {
                                     Objects.requireNonNull(providerInfo);
                                 }
                                 provider.setInfoLocked(info);
@@ -4678,6 +4704,9 @@
                     }
                     if (newInfo != null) {
                         info = newInfo;
+                        if (DEBUG) {
+                            Objects.requireNonNull(info);
+                        }
                         updateGeneratedPreviewCategoriesLocked();
                     }
                 }
@@ -4699,12 +4728,18 @@
         @GuardedBy("AppWidgetServiceImpl.mLock")
         public void setPartialInfoLocked(AppWidgetProviderInfo info) {
             this.info = info;
+            if (DEBUG) {
+                Objects.requireNonNull(this.info);
+            }
             mInfoParsed = false;
         }
 
         @GuardedBy("AppWidgetServiceImpl.mLock")
         public void setInfoLocked(AppWidgetProviderInfo info) {
             this.info = info;
+            if (DEBUG) {
+                Objects.requireNonNull(this.info);
+            }
             mInfoParsed = true;
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index 468b9ab..219b788 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -36,6 +36,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
 import com.android.internal.inputmethod.IInlineSuggestionsResponseCallback;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
 import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
 import com.android.server.autofill.ui.InlineFillUi;
 import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -376,8 +377,8 @@
     /**
      * Internal implementation of {@link IInlineSuggestionsRequestCallback}.
      */
-    private static final class InlineSuggestionsRequestCallbackImpl extends
-            IInlineSuggestionsRequestCallback.Stub {
+    private static final class InlineSuggestionsRequestCallbackImpl
+            implements InlineSuggestionsRequestCallback {
 
         private final WeakReference<AutofillInlineSuggestionsRequestSession> mSession;
 
@@ -388,7 +389,7 @@
 
         @BinderThread
         @Override
-        public void onInlineSuggestionsUnsupported() throws RemoteException {
+        public void onInlineSuggestionsUnsupported() {
             if (sDebug) Slog.d(TAG, "onInlineSuggestionsUnsupported() called.");
             final AutofillInlineSuggestionsRequestSession session = mSession.get();
             if (session != null) {
@@ -412,7 +413,7 @@
         }
 
         @Override
-        public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
+        public void onInputMethodStartInput(AutofillId imeFieldId) {
             if (sVerbose) Slog.v(TAG, "onInputMethodStartInput() received on " + imeFieldId);
             final AutofillInlineSuggestionsRequestSession session = mSession.get();
             if (session != null) {
@@ -423,7 +424,7 @@
         }
 
         @Override
-        public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
+        public void onInputMethodShowInputRequested(boolean requestResult) {
             if (sVerbose) {
                 Slog.v(TAG, "onInputMethodShowInputRequested() received: " + requestResult);
             }
@@ -454,7 +455,7 @@
         }
 
         @Override
-        public void onInputMethodFinishInput() throws RemoteException {
+        public void onInputMethodFinishInput() {
             if (sVerbose) Slog.v(TAG, "onInputMethodFinishInput() received");
             final AutofillInlineSuggestionsRequestSession session = mSession.get();
             if (session != null) {
@@ -466,7 +467,7 @@
 
         @BinderThread
         @Override
-        public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
+        public void onInlineSuggestionsSessionInvalidated() {
             if (sDebug) Slog.d(TAG, "onInlineSuggestionsSessionInvalidated() called.");
             final AutofillInlineSuggestionsRequestSession session = mSession.get();
             if (session != null) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 07b16c5..a8b1235 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1168,6 +1168,7 @@
         return null;
     }
 
+    @GuardedBy("mLock")
     @Nullable
     private AutofillValue findValueFromThisSessionOnlyLocked(@NonNull AutofillId autofillId) {
         final ViewState state = mViewStates.get(autofillId);
@@ -1176,9 +1177,25 @@
             return null;
         }
         AutofillValue value = state.getCurrentValue();
+
+        // Some app clears the form before navigating to another activities. In this case, use the
+        // cached value instead.
+        if (value == null || value.isEmpty()) {
+            AutofillValue candidateSaveValue = state.getCandidateSaveValue();
+            if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
+                if (sDebug) {
+                    Slog.d(TAG, "findValueLocked(): current value for " + autofillId
+                            + " is empty, using candidateSaveValue instead.");
+                }
+                return candidateSaveValue;
+            }
+        }
         if (value == null) {
-            if (sDebug) Slog.d(TAG, "findValueLocked(): no current value for " + autofillId);
-            value = getValueFromContextsLocked(autofillId);
+            if (sDebug) {
+                Slog.d(TAG, "findValueLocked(): no current value for " + autofillId
+                        + ", checking value from previous fill contexts");
+                value = getValueFromContextsLocked(autofillId);
+            }
         }
         return value;
     }
@@ -3717,19 +3734,34 @@
 
                 AutofillValue value = viewState.getCurrentValue();
                 if (value == null || value.isEmpty()) {
-                    final AutofillValue initialValue = getValueFromContextsLocked(id);
-                    if (initialValue != null) {
-                        if (sDebug) {
-                            Slog.d(TAG, "Value of required field " + id + " didn't change; "
-                                    + "using initial value (" + initialValue + ") instead");
+                    // Some apps clear the form before navigating to other activities.
+                    // If current value is empty, consider fall back to last cached
+                    // non-empty result first.
+                    final AutofillValue candidateSaveValue =
+                            viewState.getCandidateSaveValue();
+                    if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
+                        if (sVerbose) {
+                            Slog.v(TAG, "current value is empty, using cached last non-empty "
+                                    + "value instead");
                         }
-                        value = initialValue;
+                        value = candidateSaveValue;
                     } else {
-                        if (sDebug) {
-                            Slog.d(TAG, "empty value for required " + id );
+                        // If candidate save value is also empty, consider falling back to initial
+                        // value in context.
+                        final AutofillValue initialValue = getValueFromContextsLocked(id);
+                        if (initialValue != null) {
+                            if (sDebug) {
+                                Slog.d(TAG, "Value of required field " + id + " didn't change; "
+                                        + "using initial value (" + initialValue + ") instead");
+                            }
+                            value = initialValue;
+                        } else {
+                            if (sDebug) {
+                                Slog.d(TAG, "empty value for required " + id);
+                            }
+                            allRequiredAreNotEmpty = false;
+                            break;
                         }
-                        allRequiredAreNotEmpty = false;
-                        break;
                     }
                 }
 
@@ -3801,7 +3833,21 @@
                         continue;
                     }
                     if ((viewState.getState() & ViewState.STATE_CHANGED) != 0) {
-                        final AutofillValue currentValue = viewState.getCurrentValue();
+                        AutofillValue currentValue = viewState.getCurrentValue();
+                        if (currentValue == null || currentValue.isEmpty()) {
+                            // Some apps clear the form before navigating to other activities.
+                            // If current value is empty, consider fall back to last cached
+                            // non-empty result instead.
+                            final AutofillValue candidateSaveValue =
+                                    viewState.getCandidateSaveValue();
+                            if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
+                                if (sVerbose) {
+                                    Slog.v(TAG, "current value is empty, using cached last "
+                                            + "non-empty value instead");
+                                }
+                                currentValue = candidateSaveValue;
+                            }
+                        }
                         final AutofillValue value = getSanitizedValue(sanitizers, id, currentValue);
                         if (value == null) {
                             if (sDebug) {
@@ -4714,14 +4760,18 @@
     @GuardedBy("mLock")
     private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value,
             ViewState viewState, int flags) {
+        // Cache the last non-empty value for save purpose. Some apps clear the form before
+        // navigating to other activities.
         if (mIgnoreViewStateResetToEmpty && (value == null || value.isEmpty())
                 && viewState.getCurrentValue() != null && viewState.getCurrentValue().isText()
                 && viewState.getCurrentValue().getTextValue() != null
                 && viewState.getCurrentValue().getTextValue().length() > 1) {
             if (sVerbose) {
-                Slog.v(TAG, "Ignoring view state reset to empty on id " + id);
+                Slog.v(TAG, "value is resetting to empty, caching the last non-empty value");
             }
-            return;
+            viewState.setCandidateSaveValue(viewState.getCurrentValue());
+        } else {
+            viewState.setCandidateSaveValue(null);
         }
         final String textValue;
         if (value == null || !value.isText()) {
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index fec5aa5..6ad0eb6 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -106,6 +106,15 @@
      */
     private FillResponse mSecondaryFillResponse;
     private AutofillValue mCurrentValue;
+
+    /**
+     * Some apps clear the form before navigating to another activity. The mCandidateSaveValue
+     * caches the value when a field with string longer than 2 characters are cleared.
+     *
+     * When showing save UI, if mCurrentValue of view state is empty, session would use
+     * mCandidateSaveValue to prompt save instead.
+     */
+    private AutofillValue mCandidateSaveValue;
     private AutofillValue mAutofilledValue;
     private AutofillValue mSanitizedValue;
     private Rect mVirtualBounds;
@@ -139,6 +148,18 @@
         mCurrentValue = value;
     }
 
+    /**
+     * Gets the candidate save value of the view.
+     */
+    @Nullable
+    AutofillValue getCandidateSaveValue() {
+        return mCandidateSaveValue;
+    }
+
+    void setCandidateSaveValue(AutofillValue value) {
+        mCandidateSaveValue = value;
+    }
+
     @Nullable
     AutofillValue getAutofilledValue() {
         return mAutofilledValue;
@@ -268,6 +289,9 @@
         if (mCurrentValue != null) {
             builder.append(", currentValue:" ).append(mCurrentValue);
         }
+        if (mCandidateSaveValue != null) {
+            builder.append(", candidateSaveValue:").append(mCandidateSaveValue);
+        }
         if (mAutofilledValue != null) {
             builder.append(", autofilledValue:" ).append(mAutofilledValue);
         }
@@ -302,6 +326,9 @@
         if (mAutofilledValue != null) {
             pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
         }
+        if (mCandidateSaveValue != null) {
+            pw.print(prefix); pw.print("candidateSaveValue:"); pw.println(mCandidateSaveValue);
+        }
         if (mSanitizedValue != null) {
             pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
         }
diff --git a/services/backup/java/com/android/server/backup/transport/TransportConnection.java b/services/backup/java/com/android/server/backup/transport/TransportConnection.java
index 1009787..67ebb3e 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportConnection.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportConnection.java
@@ -658,11 +658,13 @@
      * This class is a proxy to TransportClient methods that doesn't hold a strong reference to the
      * TransportClient, allowing it to be GC'ed. If the reference was lost it logs a message.
      */
-    private static class TransportConnectionMonitor implements ServiceConnection {
+    @VisibleForTesting
+    static class TransportConnectionMonitor implements ServiceConnection {
         private final Context mContext;
         private final WeakReference<TransportConnection> mTransportClientRef;
 
-        private TransportConnectionMonitor(Context context,
+        @VisibleForTesting
+        TransportConnectionMonitor(Context context,
                 TransportConnection transportConnection) {
             mContext = context;
             mTransportClientRef = new WeakReference<>(transportConnection);
@@ -704,7 +706,13 @@
 
         /** @see TransportConnection#finalize() */
         private void referenceLost(String caller) {
-            mContext.unbindService(this);
+            try {
+                mContext.unbindService(this);
+            } catch (IllegalArgumentException e) {
+                TransportUtils.log(Priority.WARN, TAG,
+                        caller + " called but unbindService failed: " + e.getMessage());
+                return;
+            }
             TransportUtils.log(
                     Priority.INFO,
                     TAG,
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 9069689..026d29c 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -135,7 +135,7 @@
      */
     public PendingIntent buildPermissionTransferUserConsentIntent(String packageName,
             @UserIdInt int userId, int associationId) {
-        if (PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName)) {
+        if (PackageUtils.isPermSyncAutoEnabled(mContext, mPackageManager, packageName)) {
             Slog.i(LOG_TAG, "User consent Intent should be skipped. Returning null.");
             // Auto enable perm sync for the allowlisted packages, but don't override user decision
             PermissionSyncRequest request = getPermissionSyncRequest(associationId);
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index 0e66fbc..71a1822 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -23,6 +23,7 @@
 import android.os.Build;
 import android.util.Slog;
 
+import com.google.security.cryptauth.lib.securegcm.ukey2.AlertException;
 import com.google.security.cryptauth.lib.securegcm.ukey2.BadHandleException;
 import com.google.security.cryptauth.lib.securegcm.ukey2.CryptoException;
 import com.google.security.cryptauth.lib.securegcm.ukey2.D2DConnectionContextV1;
@@ -203,7 +204,8 @@
      *
      * This method must only be called from one of the two participants.
      */
-    public void establishSecureConnection() throws IOException, SecureChannelException {
+    public void establishSecureConnection() throws IOException,
+            SecureChannelException, HandshakeException {
         if (isSecured()) {
             Slog.d(TAG, "Channel is already secure.");
             return;
@@ -334,7 +336,7 @@
         }
     }
 
-    private void initiateHandshake() throws IOException, BadHandleException {
+    private void initiateHandshake() throws IOException, BadHandleException , HandshakeException {
         if (mConnectionContext != null) {
             Slog.d(TAG, "Ukey2 handshake is already completed.");
             return;
@@ -394,8 +396,8 @@
         }
     }
 
-    private void exchangeHandshake()
-            throws IOException, HandshakeException, BadHandleException, CryptoException {
+    private void exchangeHandshake() throws IOException, HandshakeException,
+            BadHandleException, CryptoException, AlertException {
         if (mConnectionContext != null) {
             Slog.d(TAG, "Ukey2 handshake is already completed.");
             return;
diff --git a/services/companion/java/com/android/server/companion/utils/PackageUtils.java b/services/companion/java/com/android/server/companion/utils/PackageUtils.java
index 254d28b..94ab9dd 100644
--- a/services/companion/java/com/android/server/companion/utils/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PackageUtils.java
@@ -21,6 +21,11 @@
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.os.Binder.getCallingUid;
 
+import static com.android.internal.R.array.config_companionDeviceCerts;
+import static com.android.internal.R.array.config_companionDevicePackages;
+import static com.android.internal.R.array.config_companionPermSyncEnabledCerts;
+import static com.android.internal.R.array.config_companionPermSyncEnabledPackages;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -185,15 +190,30 @@
      */
     public static boolean isPackageAllowlisted(Context context,
             PackageManagerInternal packageManagerInternal, @NonNull String packageName) {
-        final String[] allowlistedPackages = context.getResources()
-                .getStringArray(com.android.internal.R.array.config_companionDevicePackages);
+        return isPackageAllowlisted(context, packageManagerInternal, packageName,
+                config_companionDevicePackages, config_companionDeviceCerts);
+    }
+
+    /**
+     * Check if perm sync is allowlisted and auto-enabled for the package.
+     */
+    public static boolean isPermSyncAutoEnabled(Context context,
+            PackageManagerInternal packageManagerInternal, String packageName) {
+        return isPackageAllowlisted(context, packageManagerInternal, packageName,
+                config_companionPermSyncEnabledPackages, config_companionPermSyncEnabledCerts);
+    }
+
+    private static boolean isPackageAllowlisted(Context context,
+            PackageManagerInternal packageManagerInternal, String packageName,
+            int packagesConfig, int certsConfig) {
+        final String[] allowlistedPackages = context.getResources().getStringArray(packagesConfig);
         if (!ArrayUtils.contains(allowlistedPackages, packageName)) {
             Slog.d(TAG, packageName + " is not allowlisted.");
             return false;
         }
 
         final String[] allowlistedPackagesSignatureDigests = context.getResources()
-                .getStringArray(com.android.internal.R.array.config_companionDeviceCerts);
+                .getStringArray(certsConfig);
         final Set<String> allowlistedSignatureDigestsForRequestingPackage = new HashSet<>();
         for (int i = 0; i < allowlistedPackages.length; i++) {
             if (allowlistedPackages[i].equals(packageName)) {
diff --git a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
index 340bc32..caa877c 100644
--- a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
+++ b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
@@ -81,9 +81,6 @@
       "name": "CtsPermissionTestCases",
       "options": [
         {
-          "include-filter": "android.permissionmultidevice.cts.DeviceAwarePermissionGrantTest"
-        },
-        {
           "include-filter": "android.permission.cts.DevicePermissionsTest"
         },
         {
@@ -93,6 +90,14 @@
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
+    },
+    {
+      "name": "CtsPermissionMultiDeviceTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
     }
   ]
 }
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index 9a73a2d..f5db6e9 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -27,9 +27,9 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
-import static com.android.server.contextualsearch.flags.Flags.enableExcludePersistentUi;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
 
@@ -286,13 +286,11 @@
         }
         final ScreenCapture.ScreenshotHardwareBuffer shb;
         if (mWmInternal != null) {
-            if (enableExcludePersistentUi()) {
-                shb = mWmInternal.takeAssistScreenshot(
-                        Set.of(TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL));
-            } else {
-                shb = mWmInternal.takeAssistScreenshot(/* windowTypesToExclude= */ Set.of());
-            }
-
+            shb = mWmInternal.takeAssistScreenshot(Set.of(
+                    TYPE_STATUS_BAR,
+                    TYPE_NAVIGATION_BAR,
+                    TYPE_NAVIGATION_BAR_PANEL,
+                    TYPE_POINTER));
         } else {
             shb = null;
         }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index d153c18..0fdf6d0 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -232,6 +232,7 @@
         "android.hardware.rebootescrow-V1-java",
         "android.hardware.power.stats-V2-java",
         "android.hidl.manager-V1.2-java",
+        "audio-permission-aidl-java",
         "cbor-java",
         "com.android.media.audio-aconfig-java",
         "icu4j_calendar_astronomer",
@@ -259,7 +260,6 @@
         "connectivity_flags_lib",
         "dreams_flags_lib",
         "aconfig_new_storage_flags_lib",
-        "aconfigd_java_proto_lib",
     ],
     javac_shard_size: 50,
     javacflags: [
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index c7a8369..d9e6186 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -121,7 +121,6 @@
             SystemProperties.getBoolean("pinner.use_pinlist", true);
 
     private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
-    private static final int MAX_HOME_PIN_SIZE = 6 * (1 << 20); // 6MB max for home app.
     private static final int MAX_ASSISTANT_PIN_SIZE = 60 * (1 << 20); // 60MB max for assistant app.
 
     public static final String ANON_REGION_STAT_NAME = "[anon]";
@@ -176,7 +175,7 @@
 
     // Resource-configured pinner flags;
     private final boolean mConfiguredToPinCamera;
-    private final boolean mConfiguredToPinHome;
+    private final int mConfiguredHomePinBytes;
     private final boolean mConfiguredToPinAssistant;
     private final int mConfiguredWebviewPinBytes;
 
@@ -238,8 +237,8 @@
         mDeviceConfigInterface = mInjector.getDeviceConfigInterface();
         mConfiguredToPinCamera = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_pinnerCameraApp);
-        mConfiguredToPinHome = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_pinnerHomeApp);
+        mConfiguredHomePinBytes = context.getResources().getInteger(
+                com.android.internal.R.integer.config_pinnerHomePinBytes);
         mConfiguredToPinAssistant = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_pinnerAssistantApp);
         mConfiguredWebviewPinBytes = context.getResources().getInteger(
@@ -390,7 +389,7 @@
                     @Override
                     public void onChange(boolean selfChange, Uri uri) {
                         if (userSetupCompleteUri.equals(uri)) {
-                            if (mConfiguredToPinHome) {
+                            if (mConfiguredHomePinBytes > 0) {
                                 sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(),
                                         true /* force */);
                             }
@@ -594,7 +593,7 @@
             Slog.i(TAG, "Pinner - skip pinning camera app");
         }
 
-        if (mConfiguredToPinHome) {
+        if (mConfiguredHomePinBytes > 0) {
             pinKeys.add(KEY_HOME);
         }
         if (mConfiguredToPinAssistant) {
@@ -815,7 +814,7 @@
             case KEY_CAMERA:
                 return MAX_CAMERA_PIN_SIZE;
             case KEY_HOME:
-                return MAX_HOME_PIN_SIZE;
+                return mConfiguredHomePinBytes;
             case KEY_ASSISTANT:
                 return MAX_ASSISTANT_PIN_SIZE;
             default:
@@ -871,6 +870,7 @@
             }
             synchronized (this) {
                 pinnedApp.mFiles.add(pf);
+                mPinnedFiles.put(pf.fileName, pf);
             }
 
             apkPinSizeLimit -= pf.bytesPinned;
@@ -1342,18 +1342,6 @@
 
     public List<PinnedFileStat> getPinnerStats() {
         ArrayList<PinnedFileStat> stats = new ArrayList<>();
-        Collection<PinnedApp> pinnedApps;
-        synchronized(this) {
-            pinnedApps = mPinnedApps.values();
-        }
-        for (PinnedApp pinnedApp : pinnedApps) {
-            for (PinnedFile pf : pinnedApp.mFiles) {
-                PinnedFileStat stat =
-                        new PinnedFileStat(pf.fileName, pf.bytesPinned, pf.groupName);
-                stats.add(stat);
-            }
-        }
-
         Collection<PinnedFile> pinnedFiles;
         synchronized(this) {
             pinnedFiles = mPinnedFiles.values();
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index a508ebf..8c1bb3b 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -1783,7 +1783,13 @@
                 String gidStr = parser.getAttributeValue(null, "gid");
                 if (gidStr != null) {
                     int gid = Process.getGidForName(gidStr);
-                    perm.gids = appendInt(perm.gids, gid);
+                    if (gid != -1) {
+                        perm.gids = appendInt(perm.gids, gid);
+                    } else {
+                        Slog.w(TAG, "<group> with unknown gid \""
+                                + gidStr + " for permission " + name + " in "
+                                + parser.getPositionDescription());
+                    }
                 } else {
                     Slog.w(TAG, "<group> without gid at "
                             + parser.getPositionDescription());
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 5933639..a3b6d80 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -134,6 +134,13 @@
         },
         {
             "name": "CtsSuspendAppsTestCases"
+        },
+        {
+            "name": "CtsWindowManagerBackgroundActivityTestCases",
+            "file_patterns": [
+                "Background.*\\.java",
+                "Activity.*\\.java"
+            ]
         }
     ],
     "presubmit-large": [
@@ -187,6 +194,18 @@
         },
         {
             "name": "SelinuxFrameworksTests"
+        },
+        {
+            "name": "WmTests",
+            "file_patterns": [
+                "Background.*\\.java",
+                "Activity.*\\.java"
+            ],
+            "options": [
+                {
+                    "include-filter": "com.android.server.wm.BackgroundActivityStart*"
+                }
+            ]
         }
-    ]
+   ]
 }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index f1776f4..f1d3584 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -1986,11 +1986,9 @@
         // the status bar should be totally disabled, the calls below will
         // have no effect until the device is unlocked.
         if (mStatusBarManager != null) {
-            StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-            if (mCarModeEnabled) {
-                info.setNotificationTickerDisabled(true);
-            }
-            mStatusBarManager.requestDisabledComponent(info, "adjustStatusBarCarModeLocked");
+            mStatusBarManager.disable(mCarModeEnabled
+                    ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
+                    : StatusBarManager.DISABLE_NONE);
         }
 
         if (mNotificationManager == null) {
diff --git a/services/core/java/com/android/server/WallpaperUpdateReceiver.java b/services/core/java/com/android/server/WallpaperUpdateReceiver.java
index 2812233..42391a5 100644
--- a/services/core/java/com/android/server/WallpaperUpdateReceiver.java
+++ b/services/core/java/com/android/server/WallpaperUpdateReceiver.java
@@ -24,7 +24,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Bitmap;
 import android.os.AsyncTask;
 import android.os.ParcelFileDescriptor;
 import android.util.Slog;
@@ -59,10 +58,10 @@
                 return;
             }
             if (DEBUG) Slog.d(TAG, "Set customized default_wallpaper.");
-            Bitmap blank = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
-            // set a blank wallpaper to force a redraw of default_wallpaper
-            wallpaperManager.setBitmap(blank);
-            wallpaperManager.setResource(com.android.internal.R.drawable.default_wallpaper);
+            // Check if it is not a live wallpaper set
+            if (wallpaperManager.getWallpaperInfo() == null) {
+                wallpaperManager.clearWallpaper();
+            }
         } catch (Exception e) {
             Slog.w(TAG, "Failed to customize system wallpaper." + e);
         }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 1015ad9..ac9ed0d 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -4532,8 +4532,13 @@
     public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
         int callingUid = Binder.getCallingUid();
         mAppOpsManager.checkPackage(callingUid, opPackageName);
-        return getAccountsAsUserForPackage(type, userId, opPackageName /* callingPackage */, -1,
-                opPackageName, false /* includeUserManagedNotVisible */);
+        try {
+            return getAccountsAsUserForPackage(type, userId, opPackageName /* callingPackage */, -1,
+                    opPackageName, false /* includeUserManagedNotVisible */);
+        } catch (SQLiteCantOpenDatabaseException e) {
+            Log.e(TAG, "Could not get accounts for user " + userId, e);
+            return new Account[]{};
+        }
     }
 
     @NonNull
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 94bf813..9be0e1f 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3783,6 +3783,14 @@
         return fgsInfo.getLastFgsStartTime() + Math.max(0, timeLimit - fgsInfo.getTotalRuntime());
     }
 
+    private TimeLimitedFgsInfo getFgsTimeLimitedInfo(int uid, int fgsType) {
+        final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(uid);
+        if (fgsInfo != null) {
+            return fgsInfo.get(fgsType);
+        }
+        return null;
+    }
+
     private void maybeUpdateFgsTrackingLocked(ServiceRecord sr, int previousFgsType) {
         final int previouslyTimeLimitedType = getTimeLimitedFgsType(previousFgsType);
         if (previouslyTimeLimitedType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE
@@ -3793,16 +3801,12 @@
 
         if (previouslyTimeLimitedType != ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE) {
             // FGS is switching types and the previous type was time-limited so update the runtime.
-            final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid);
-            if (fgsInfo != null) {
-                final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(previouslyTimeLimitedType);
-                if (fgsTypeInfo != null) {
-                    // Update the total runtime for the previous time-limited fgs type.
-                    fgsTypeInfo.updateTotalRuntime();
-                    // TODO(b/330399444): handle the case where an app is running 2 services of the
-                    //  same time-limited type in parallel and stops one of them which leads to the
-                    //  second running one gaining additional runtime.
-                }
+            final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(
+                                                    sr.appInfo.uid, previouslyTimeLimitedType);
+            if (fgsTypeInfo != null) {
+                // Update the total runtime for the previous time-limited fgs type.
+                fgsTypeInfo.updateTotalRuntime(SystemClock.uptimeMillis());
+                fgsTypeInfo.decNumParallelServices();
             }
 
             if (!sr.isFgsTimeLimited()) {
@@ -3825,10 +3829,10 @@
         final int timeLimitedFgsType = getTimeLimitedFgsType(sr.foregroundServiceType);
         TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(timeLimitedFgsType);
         if (fgsTypeInfo == null) {
-            fgsTypeInfo = sr.createTimeLimitedFgsInfo(nowUptime);
+            fgsTypeInfo = sr.createTimeLimitedFgsInfo();
             fgsInfo.put(timeLimitedFgsType, fgsTypeInfo);
         }
-        fgsTypeInfo.setLastFgsStartTime(nowUptime);
+        fgsTypeInfo.noteFgsFgsStart(nowUptime);
 
         // We'll cancel the previous ANR timer and start a fresh one below.
         mFGSAnrTimer.cancel(sr);
@@ -3852,13 +3856,12 @@
             return; // if the current fgs type is not time-limited, return.
         }
 
-        final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid);
-        if (fgsInfo != null) {
-            final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(timeLimitedType);
-            if (fgsTypeInfo != null) {
-                // Update the total runtime for the previous time-limited fgs type.
-                fgsTypeInfo.updateTotalRuntime();
-            }
+        final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(
+                                                sr.appInfo.uid, timeLimitedType);
+        if (fgsTypeInfo != null) {
+            // Update the total runtime for the previous time-limited fgs type.
+            fgsTypeInfo.updateTotalRuntime(SystemClock.uptimeMillis());
+            fgsTypeInfo.decNumParallelServices();
         }
         Slog.d(TAG_SERVICE, "Stop FGS timeout: " + sr);
         mFGSAnrTimer.cancel(sr);
@@ -3913,24 +3916,21 @@
             mFGSAnrTimer.accept(sr);
             traceInstant("FGS timed out: ", sr);
 
-            final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid);
-            if (fgsInfo != null) {
-                final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(fgsType);
-                if (fgsTypeInfo != null) {
-                    // Update total runtime for the time-limited fgs type and mark it as timed out.
-                    fgsTypeInfo.updateTotalRuntime();
-                    fgsTypeInfo.setTimeLimitExceededAt(nowUptime);
+            final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(sr.appInfo.uid, fgsType);
+            if (fgsTypeInfo != null) {
+                // Update total runtime for the time-limited fgs type and mark it as timed out.
+                fgsTypeInfo.updateTotalRuntime(nowUptime);
+                fgsTypeInfo.setTimeLimitExceededAt(nowUptime);
 
-                    logFGSStateChangeLocked(sr,
-                            FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
-                            nowUptime > fgsTypeInfo.getLastFgsStartTime()
-                                    ? (int) (nowUptime - fgsTypeInfo.getLastFgsStartTime()) : 0,
-                            FGS_STOP_REASON_UNKNOWN,
-                            FGS_TYPE_POLICY_CHECK_UNKNOWN,
-                            FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
-                            false /* fgsRestrictionRecalculated */
-                    );
-                }
+                logFGSStateChangeLocked(sr,
+                        FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
+                        nowUptime > fgsTypeInfo.getFirstFgsStartUptime()
+                                ? (int) (nowUptime - fgsTypeInfo.getFirstFgsStartUptime()) : 0,
+                        FGS_STOP_REASON_UNKNOWN,
+                        FGS_TYPE_POLICY_CHECK_UNKNOWN,
+                        FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+                        false /* fgsRestrictionRecalculated */
+                );
             }
 
             try {
@@ -3949,6 +3949,16 @@
         if (fgsType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE) {
             return; // no timed out FGS type was found (either it was stopped or it switched types)
         }
+
+        synchronized (mAm) {
+            final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(sr.appInfo.uid, fgsType);
+            if (fgsTypeInfo != null) {
+                // Runtime is already updated when the service times out - if the app didn't
+                // stop the service, decrement the number of parallel running services here.
+                fgsTypeInfo.decNumParallelServices();
+            }
+        }
+
         final String reason = "A foreground service of type "
                 + ServiceInfo.foregroundServiceTypeToLabel(fgsType)
                 + " did not stop within its timeout: " + sr.getComponentName();
@@ -3958,7 +3968,6 @@
             Slog.wtf(TAG, reason);
             return;
         }
-
         if (android.app.Flags.enableFgsTimeoutCrashBehavior()) {
             // Crash the app
             synchronized (mAm) {
@@ -4005,7 +4014,6 @@
 
     private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) {
         maybeStopShortFgsTimeoutLocked(service);
-        maybeStopFgsTimeoutLocked(service);
         final ProcessServiceRecord psr = service.app.mServices;
         psr.stopService(service);
         psr.updateBoundClientUids();
@@ -6291,7 +6299,6 @@
             Slog.w(TAG_SERVICE, "Short FGS brought down without stopping: " + r);
             maybeStopShortFgsTimeoutLocked(r);
         }
-        maybeStopFgsTimeoutLocked(r);
 
         // Report to all of the connections that the service is no longer
         // available.
@@ -6416,7 +6423,6 @@
         final boolean exitingFg = r.isForeground;
         if (exitingFg) {
             maybeStopShortFgsTimeoutLocked(r);
-            maybeStopFgsTimeoutLocked(r);
             decActiveForegroundAppLocked(smap, r);
             synchronized (mAm.mProcessStats.mLock) {
                 ServiceState stracker = r.getTracker();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 46ed1fd..00d8efa 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -598,6 +598,9 @@
     // as one line, but close enough for now.
     static final int RESERVED_BYTES_PER_LOGCAT_LINE = 100;
 
+    // How many seconds should the system wait before terminating the spawned logcat process.
+    static final int LOGCAT_TIMEOUT_SEC = 10;
+
     // Necessary ApplicationInfo flags to mark an app as persistent
     static final int PERSISTENT_MASK =
             ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
@@ -9939,126 +9942,70 @@
         // If process is null, we are being called from some internal code
         // and may be about to die -- run this synchronously.
         final boolean runSynchronously = process == null;
-        Thread worker =
-                new Thread("Error dump: " + dropboxTag) {
-                    @Override
-                    public void run() {
-                        if (report != null) {
-                            sb.append(report);
+        Thread worker = new Thread("Error dump: " + dropboxTag) {
+            @Override
+            public void run() {
+                if (report != null) {
+                    sb.append(report);
+                }
+
+                String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
+                String kerLogSetting = Settings.Global.ERROR_KERNEL_LOG_PREFIX + dropboxTag;
+                String maxBytesSetting = Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag;
+                int logcatLines = Build.IS_USER
+                        ? 0
+                        : Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0);
+                int kernelLogLines = Build.IS_USER
+                        ? 0
+                        : Settings.Global.getInt(mContext.getContentResolver(), kerLogSetting, 0);
+                int dropboxMaxSize = Settings.Global.getInt(
+                        mContext.getContentResolver(), maxBytesSetting, DROPBOX_DEFAULT_MAX_SIZE);
+
+                if (dataFile != null) {
+                    // Attach the stack traces file to the report so collectors can load them
+                    // by file if they have access.
+                    sb.append(DATA_FILE_PATH_HEADER)
+                            .append(dataFile.getAbsolutePath()).append('\n');
+
+                    int maxDataFileSize = dropboxMaxSize
+                            - sb.length()
+                            - logcatLines * RESERVED_BYTES_PER_LOGCAT_LINE
+                            - kernelLogLines * RESERVED_BYTES_PER_LOGCAT_LINE
+                            - DATA_FILE_PATH_FOOTER.length();
+
+                    if (maxDataFileSize > 0) {
+                        // Inline dataFile contents if there is room.
+                        try {
+                            sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize,
+                                    "\n\n[[TRUNCATED]]\n"));
+                        } catch (IOException e) {
+                            Slog.e(TAG, "Error reading " + dataFile, e);
                         }
-
-                        String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
-                        String maxBytesSetting =
-                                Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag;
-                        int lines =
-                                Build.IS_USER
-                                        ? 0
-                                        : Settings.Global.getInt(
-                                                mContext.getContentResolver(), logcatSetting, 0);
-                        int dropboxMaxSize =
-                                Settings.Global.getInt(
-                                        mContext.getContentResolver(),
-                                        maxBytesSetting,
-                                        DROPBOX_DEFAULT_MAX_SIZE);
-
-                        if (dataFile != null) {
-                            // Attach the stack traces file to the report so collectors can load
-                            // them
-                            // by file if they have access.
-                            sb.append(DATA_FILE_PATH_HEADER)
-                                    .append(dataFile.getAbsolutePath())
-                                    .append('\n');
-
-                            int maxDataFileSize =
-                                    dropboxMaxSize
-                                            - sb.length()
-                                            - lines * RESERVED_BYTES_PER_LOGCAT_LINE
-                                            - DATA_FILE_PATH_FOOTER.length();
-
-                            if (maxDataFileSize > 0) {
-                                // Inline dataFile contents if there is room.
-                                try {
-                                    sb.append(
-                                            FileUtils.readTextFile(
-                                                    dataFile,
-                                                    maxDataFileSize,
-                                                    "\n\n[[TRUNCATED]]\n"));
-                                } catch (IOException e) {
-                                    Slog.e(TAG, "Error reading " + dataFile, e);
-                                }
-                            }
-
-                            // Always append the footer, even there wasn't enough space to inline
-                            // the
-                            // dataFile contents.
-                            sb.append(DATA_FILE_PATH_FOOTER);
-                        }
-
-                        if (crashInfo != null && crashInfo.stackTrace != null) {
-                            sb.append(crashInfo.stackTrace);
-                        }
-
-                        if (lines > 0 && !runSynchronously) {
-                            sb.append("\n");
-
-                            InputStreamReader input = null;
-                            try {
-                                java.lang.Process logcat =
-                                        new ProcessBuilder(
-                                                        // Time out after 10s of inactivity, but
-                                                        // kill logcat with SEGV
-                                                        // so we can investigate why it didn't
-                                                        // finish.
-                                                        "/system/bin/timeout",
-                                                        "-i",
-                                                        "-s",
-                                                        "SEGV",
-                                                        "10s",
-                                                        // Merge several logcat streams, and take
-                                                        // the last N lines.
-                                                        "/system/bin/logcat",
-                                                        "-v",
-                                                        "threadtime",
-                                                        "-b",
-                                                        "events",
-                                                        "-b",
-                                                        "system",
-                                                        "-b",
-                                                        "main",
-                                                        "-b",
-                                                        "crash",
-                                                        "-t",
-                                                        String.valueOf(lines))
-                                                .redirectErrorStream(true)
-                                                .start();
-
-                                try {
-                                    logcat.getOutputStream().close();
-                                } catch (IOException e) {
-                                }
-                                try {
-                                    logcat.getErrorStream().close();
-                                } catch (IOException e) {
-                                }
-                                input = new InputStreamReader(logcat.getInputStream());
-
-                                int num;
-                                char[] buf = new char[8192];
-                                while ((num = input.read(buf)) > 0) sb.append(buf, 0, num);
-                            } catch (IOException e) {
-                                Slog.e(TAG, "Error running logcat", e);
-                            } finally {
-                                if (input != null)
-                                    try {
-                                        input.close();
-                                    } catch (IOException e) {
-                                    }
-                            }
-                        }
-
-                        dbox.addText(dropboxTag, sb.toString());
                     }
-                };
+                    // Always append the footer, even there wasn't enough space to inline the
+                    // dataFile contents.
+                    sb.append(DATA_FILE_PATH_FOOTER);
+                }
+
+                if (crashInfo != null && crashInfo.stackTrace != null) {
+                    sb.append(crashInfo.stackTrace);
+                }
+                boolean shouldAddLogs = logcatLines > 0 || kernelLogLines > 0;
+                if (!runSynchronously && shouldAddLogs) {
+                    sb.append("\n");
+                    if (logcatLines > 0) {
+                        fetchLogcatBuffers(sb, logcatLines, LOGCAT_TIMEOUT_SEC,
+                                List.of("events", "system", "main", "crash"));
+                    }
+                    if (kernelLogLines > 0) {
+                        fetchLogcatBuffers(sb, kernelLogLines, LOGCAT_TIMEOUT_SEC / 2,
+                                List.of("kernel"));
+                    }
+                }
+
+                dbox.addText(dropboxTag, sb.toString());
+            }
+        };
 
         if (runSynchronously) {
             final int oldMask = StrictMode.allowThreadDiskWritesMask();
@@ -10321,6 +10268,67 @@
     }
 
     /**
+     * Retrieves logs from specified logcat buffers and appends them to a StringBuilder
+     * in the supplied order. The method executes a logcat command to fetch specific
+     * log entries from the supplied buffers.
+     *
+     * @param sb the StringBuilder to append the logcat output to.
+     * @param lines the number of lines to retrieve.
+     * @param timeout the maximum allowed time in seconds for logcat to run before being terminated.
+     * @param buffers the list of log buffers from which to retrieve logs.
+     */
+    private static void fetchLogcatBuffers(StringBuilder sb, int lines,
+            int timeout, List<String> buffers) {
+
+        if (buffers.size() == 0 || lines <= 0 || timeout <= 0) {
+            return;
+        }
+
+        List<String> command = new ArrayList<>(10 + (2 * buffers.size()));
+        // Time out after 10s of inactivity, but kill logcat with SEGV
+        // so we can investigate why it didn't finish.
+        command.add("/system/bin/timeout");
+        command.add("-i");
+        command.add("-s");
+        command.add("SEGV");
+        command.add(timeout + "s");
+
+        // Merge several logcat streams, and take the last N lines.
+        command.add("/system/bin/logcat");
+        command.add("-v");
+        // This adds a timestamp and thread info to each log line.
+        command.add("threadtime");
+        for (String buffer : buffers) {
+            command.add("-b");
+            command.add(buffer);
+        }
+        // Limit the output to the last N lines.
+        command.add("-t");
+        command.add(String.valueOf(lines));
+
+        try {
+            java.lang.Process proc =
+                    new ProcessBuilder(command).redirectErrorStream(true).start();
+
+            // Close the output stream immediately as we do not send input to the process.
+            try {
+                proc.getOutputStream().close();
+            } catch (IOException e) {
+            }
+
+            try (InputStreamReader reader = new InputStreamReader(proc.getInputStream())) {
+                char[] buffer = new char[8192];
+                int numRead;
+                while ((numRead = reader.read(buffer, 0, buffer.length)) > 0) {
+                    sb.append(buffer, 0, numRead);
+                }
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Error running logcat", e);
+        }
+    }
+
+    /**
      * Check if the calling process has the permission to dump given package,
      * throw SecurityException if it doesn't have the permission.
      *
@@ -12326,8 +12334,8 @@
             ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ,
             ProcessList.PERSISTENT_SERVICE_ADJ, ProcessList.FOREGROUND_APP_ADJ,
             ProcessList.VISIBLE_APP_ADJ,
-            ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_LOW_APP_ADJ,
-            ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ,
+            ProcessList.PERCEPTIBLE_APP_ADJ,
+            ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ, ProcessList.PERCEPTIBLE_LOW_APP_ADJ,
             ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
             ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ,
             ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MIN_ADJ
@@ -12335,7 +12343,7 @@
     static final String[] DUMP_MEM_OOM_LABEL = new String[] {
             "Native",
             "System", "Persistent", "Persistent Service", "Foreground",
-            "Visible", "Perceptible", "Perceptible Low", "Perceptible Medium",
+            "Visible", "Perceptible", "Perceptible Medium", "Perceptible Low",
             "Backup", "Heavy Weight",
             "A Services", "Home",
             "Previous", "B Services", "Cached"
@@ -12343,7 +12351,7 @@
     static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] {
             "native",
             "sys", "pers", "persvc", "fore",
-            "vis", "percept", "perceptl", "perceptm",
+            "vis", "percept", "perceptm", "perceptl",
             "backup", "heavy",
             "servicea", "home",
             "prev", "serviceb", "cached"
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a182a10..bbd4323 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -130,6 +130,7 @@
 import com.android.server.am.nano.Capability;
 import com.android.server.am.nano.FrameworkCapability;
 import com.android.server.am.nano.VMCapability;
+import com.android.server.am.nano.VMInfo;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.utils.Slogf;
@@ -460,6 +461,8 @@
                 return -1;
             }
         }
+        String vmName = System.getProperty("java.vm.name", "?");
+        String vmVersion = System.getProperty("java.vm.version", "?");
 
         if (outputAsProtobuf) {
             Capabilities capabilities = new Capabilities();
@@ -486,6 +489,11 @@
                 capabilities.frameworkCapabilities[i] = cap;
             }
 
+            VMInfo vmInfo = new VMInfo();
+            vmInfo.name = vmName;
+            vmInfo.version = vmVersion;
+            capabilities.vmInfo = vmInfo;
+
             try {
                 getRawOutputStream().write(Capabilities.toByteArray(capabilities));
             } catch (IOException e) {
@@ -505,6 +513,8 @@
             for (String capability : Debug.getFeatureList()) {
                 pw.println("framework:" + capability);
             }
+            pw.println("vm_name:" + vmName);
+            pw.println("vm_version:" + vmVersion);
         }
         return 0;
     }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 4f84149..58732fd 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -159,6 +159,8 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * All information we are collecting about things that can happen that impact
@@ -409,26 +411,14 @@
                 com.android.internal.R.bool.config_batteryStatsResetOnUnplugHighBatteryLevel);
         final boolean resetOnUnplugAfterSignificantCharge = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_batteryStatsResetOnUnplugAfterSignificantCharge);
-        final long powerStatsThrottlePeriodCpu = context.getResources().getInteger(
-                com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodCpu);
-        final long powerStatsThrottlePeriodMobileRadio = context.getResources().getInteger(
-                com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodMobileRadio);
-        final long powerStatsThrottlePeriodWifi = context.getResources().getInteger(
-                com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodWifi);
-        mBatteryStatsConfig =
+        BatteryStatsImpl.BatteryStatsConfig.Builder batteryStatsConfigBuilder =
                 new BatteryStatsImpl.BatteryStatsConfig.Builder()
                         .setResetOnUnplugHighBatteryLevel(resetOnUnplugHighBatteryLevel)
-                        .setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge)
-                        .setPowerStatsThrottlePeriodMillis(
-                                BatteryConsumer.POWER_COMPONENT_CPU,
-                                powerStatsThrottlePeriodCpu)
-                        .setPowerStatsThrottlePeriodMillis(
-                                BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
-                                powerStatsThrottlePeriodMobileRadio)
-                        .setPowerStatsThrottlePeriodMillis(
-                                BatteryConsumer.POWER_COMPONENT_WIFI,
-                                powerStatsThrottlePeriodWifi)
-                        .build();
+                        .setResetOnUnplugAfterSignificantCharge(
+                                resetOnUnplugAfterSignificantCharge);
+        setPowerStatsThrottlePeriods(batteryStatsConfigBuilder, context.getResources().getString(
+                com.android.internal.R.string.config_powerStatsThrottlePeriods));
+        mBatteryStatsConfig = batteryStatsConfigBuilder.build();
         mPowerStatsUidResolver = new PowerStatsUidResolver();
         mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
                 systemDir, mHandler, this, this, mUserManagerUserInfoProvider, mPowerProfile,
@@ -515,6 +505,26 @@
         return config;
     }
 
+    private void setPowerStatsThrottlePeriods(BatteryStatsImpl.BatteryStatsConfig.Builder builder,
+            String configString) {
+        Matcher matcher = Pattern.compile("([^:]+):(\\d+)\\s*").matcher(configString);
+        while (matcher.find()) {
+            String powerComponentName = matcher.group(1);
+            long throttlePeriod;
+            try {
+                throttlePeriod = Long.parseLong(matcher.group(2));
+            } catch (NumberFormatException nfe) {
+                throw new IllegalArgumentException(
+                        "Invalid config_powerStatsThrottlePeriods format: " + configString);
+            }
+            if (powerComponentName.equals("*")) {
+                builder.setDefaultPowerStatsThrottlePeriodMillis(throttlePeriod);
+            } else {
+                builder.setPowerStatsThrottlePeriodMillis(powerComponentName, throttlePeriod);
+            }
+        }
+    }
+
     /**
      * Creates an instance of BatteryStatsService and restores data from stored state.
      */
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index f2b9b25..31704c4 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -136,6 +136,13 @@
                 false, oomAdjReason, UNKNOWN_ADJ, false, false);
     }
 
+    @Override
+    public boolean canAffectCapabilities() {
+        return hasFlag(Context.BIND_INCLUDE_CAPABILITIES
+                | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS);
+    }
+
+
     public long getFlags() {
         return flags;
     }
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index 3988277..ae5ae01 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -82,6 +82,12 @@
                 false, oomAdjReason, UNKNOWN_ADJ, false, false);
     }
 
+    @Override
+    public boolean canAffectCapabilities() {
+        return false;
+    }
+
+
     public void startAssociationIfNeeded() {
         // If we don't already have an active association, create one...  but only if this
         // is an association between two different processes.
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index 3268b66..9600317 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.PROCESS_CAPABILITY_BFSL;
 import static android.app.ActivityManager.PROCESS_STATE_BACKUP;
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
@@ -521,6 +522,11 @@
          */
         void computeHostOomAdjLSP(OomAdjuster oomAdjuster, ProcessRecord host, ProcessRecord client,
                 long now, ProcessRecord topApp, boolean doingAll, int oomAdjReason, int cachedAdj);
+
+        /**
+         * Returns true if this connection can propagate capabilities.
+         */
+        boolean canAffectCapabilities();
     }
 
     /**
@@ -553,16 +559,29 @@
      */
     private class ComputeConnectionIgnoringReachableClientsConsumer implements
             BiConsumer<Connection, ProcessRecord> {
-        public OomAdjusterArgs args = null;
+        private OomAdjusterArgs mArgs = null;
+        public boolean hasReachableClient = false;
+
+        public void init(OomAdjusterArgs args) {
+            mArgs = args;
+            hasReachableClient = false;
+        }
 
         @Override
         public void accept(Connection conn, ProcessRecord client) {
-            final ProcessRecord host = args.mApp;
-            final ProcessRecord topApp = args.mTopApp;
-            final long now = args.mNow;
-            final @OomAdjReason int oomAdjReason = args.mOomAdjReason;
+            final ProcessRecord host = mArgs.mApp;
+            final ProcessRecord topApp = mArgs.mTopApp;
+            final long now = mArgs.mNow;
+            final @OomAdjReason int oomAdjReason = mArgs.mOomAdjReason;
 
-            if (client.mState.isReachable()) return;
+            if (client.mState.isReachable()) {
+                hasReachableClient = true;
+                return;
+            }
+
+            if (unimportantConnectionLSP(conn, host, client)) {
+                return;
+            }
 
             conn.computeHostOomAdjLSP(OomAdjusterModernImpl.this, host, client, now, topApp, false,
                     oomAdjReason, UNKNOWN_ADJ);
@@ -591,6 +610,10 @@
             final int prevProcState = host.mState.getCurProcState();
             final int prevAdj = host.mState.getCurRawAdj();
 
+            if (unimportantConnectionLSP(conn, host, client)) {
+                return;
+            }
+
             conn.computeHostOomAdjLSP(OomAdjusterModernImpl.this, host, client, now, topApp,
                     fullUpdate, oomAdjReason, cachedAdj);
 
@@ -874,7 +897,7 @@
         // processes cannot change as a part of this update, their current values can be used
         // right now.
         mProcessRecordProcStateNodes.resetLastNodes();
-        initReachableStatesLSP(reachables, mTmpOomAdjusterArgs);
+        initReachableStatesLSP(reachables, targets.size(), mTmpOomAdjusterArgs);
 
         // Set adj last nodes now, this way a process will only be reevaluated during the adj node
         // iteration if they adj score changed during the procState node iteration.
@@ -914,8 +937,6 @@
     /**
      * Mark all processes reachable from the {@code reachables} processes and add them to the
      * provided {@code reachables} list (targets excluded).
-     *
-     * Returns true if a cycle exists within the reachable process graph.
      */
     @GuardedBy({"mService", "mProcLock"})
     private void collectAndMarkReachableProcessesLSP(ArrayList<ProcessRecord> reachables) {
@@ -930,8 +951,35 @@
      * Calculate initial importance states for {@code reachables} and update their slot position
      * if necessary.
      */
-    private void initReachableStatesLSP(ArrayList<ProcessRecord> reachables, OomAdjusterArgs args) {
-        for (int i = 0, size = reachables.size(); i < size; i++) {
+    private void initReachableStatesLSP(ArrayList<ProcessRecord> reachables, int targetCount,
+            OomAdjusterArgs args) {
+        int i = 0;
+        boolean initReachables = !Flags.skipUnimportantConnections();
+        for (; i < targetCount && !initReachables; i++) {
+            final ProcessRecord target = reachables.get(i);
+            final int prevProcState = target.mState.getCurProcState();
+            final int prevAdj = target.mState.getCurRawAdj();
+            final int prevCapability = target.mState.getCurCapability();
+            final boolean prevShouldNotFreeze = target.mOptRecord.shouldNotFreeze();
+
+            args.mApp = target;
+            // If target client is a reachable, reachables need to be reinited in case this
+            // client is important enough to change this target in the computeConnection step.
+            initReachables |= computeOomAdjIgnoringReachablesLSP(args);
+            // If target lowered in importance, reachables need to be reinited because this
+            // target may have been the source of a reachable's current importance.
+            initReachables |= selfImportanceLoweredLSP(target, prevProcState, prevAdj,
+                    prevCapability, prevShouldNotFreeze);
+
+            updateProcStateSlot(target, prevProcState);
+            updateAdjSlot(target, prevAdj);
+        }
+
+        if (!initReachables) {
+            return;
+        }
+
+        for (int size = reachables.size(); i < size; i++) {
             final ProcessRecord reachable = reachables.get(i);
             final int prevProcState = reachable.mState.getCurProcState();
             final int prevAdj = reachable.mState.getCurRawAdj();
@@ -948,9 +996,11 @@
      * Calculate initial importance states for {@code app}.
      * Processes not marked reachable cannot change as a part of this update, so connections from
      * those process can be calculated now.
+     *
+     * Returns true if any client connection was skipped due to a reachablity cycle.
      */
     @GuardedBy({"mService", "mProcLock"})
-    private void computeOomAdjIgnoringReachablesLSP(OomAdjusterArgs args) {
+    private boolean computeOomAdjIgnoringReachablesLSP(OomAdjusterArgs args) {
         final ProcessRecord app = args.mApp;
         final ProcessRecord topApp = args.mTopApp;
         final long now = args.mNow;
@@ -958,8 +1008,9 @@
 
         computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, false, now, false, false, oomAdjReason, false);
 
-        mComputeConnectionIgnoringReachableClientsConsumer.args = args;
+        mComputeConnectionIgnoringReachableClientsConsumer.init(args);
         forEachClientConnectionLSP(app, mComputeConnectionIgnoringReachableClientsConsumer);
+        return mComputeConnectionIgnoringReachableClientsConsumer.hasReachableClient;
     }
 
     /**
@@ -1039,6 +1090,7 @@
                     } else {
                         client = cr.binding.client;
                     }
+                    if (client == null || client == app) continue;
                     connectionConsumer.accept(cr, client);
                 }
             }
@@ -1053,4 +1105,66 @@
             }
         }
     }
+
+    /**
+     * Returns true if at least one the provided values is more important than those in {@code app}.
+     */
+    @GuardedBy({"mService", "mProcLock"})
+    private static boolean selfImportanceLoweredLSP(ProcessRecord app, int prevProcState,
+            int prevAdj, int prevCapability, boolean prevShouldNotFreeze) {
+        if (app.mState.getCurProcState() > prevProcState) {
+            return true;
+        }
+        if (app.mState.getCurRawAdj() > prevAdj)  {
+            return true;
+        }
+        if ((app.mState.getCurCapability() & prevCapability) != prevCapability)  {
+            return true;
+        }
+        if (!app.mOptRecord.shouldNotFreeze() && prevShouldNotFreeze) {
+            // No long marked as should not freeze.
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether a host connection evaluation can be skipped due to lack of importance.
+     * Note: the client and host need to be provided as well for the isolated and sandbox
+     * scenarios.
+     */
+    @GuardedBy({"mService", "mProcLock"})
+    private static boolean unimportantConnectionLSP(Connection conn,
+            ProcessRecord host, ProcessRecord client) {
+        if (!Flags.skipUnimportantConnections()) {
+            // Feature not enabled, just return false so the connection is evaluated.
+            return false;
+        }
+        if (host.mState.getCurProcState() > client.mState.getCurProcState()) {
+            return false;
+        }
+        if (host.mState.getCurRawAdj() > client.mState.getCurRawAdj())  {
+            return false;
+        }
+        final int serviceCapability = host.mState.getCurCapability();
+        final int clientCapability = client.mState.getCurCapability();
+        if ((serviceCapability & clientCapability) != clientCapability) {
+            // Client has a capability the host does not have.
+            if ((clientCapability & PROCESS_CAPABILITY_BFSL) == PROCESS_CAPABILITY_BFSL
+                    && (serviceCapability & PROCESS_CAPABILITY_BFSL) == 0) {
+                // The BFSL capability does not need a flag to propagate.
+                return false;
+            }
+            if (conn.canAffectCapabilities()) {
+                // One of these bind flags may propagate that capability.
+                return false;
+            }
+        }
+
+        if (!host.mOptRecord.shouldNotFreeze() && client.mOptRecord.shouldNotFreeze()) {
+            // If the client is marked as should not freeze, so should the host.
+            return false;
+        }
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index da45a77..8d7a1c9 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -18,6 +18,10 @@
 
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
 
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -389,13 +393,20 @@
 
     private static BackgroundStartPrivileges getBackgroundStartPrivilegesAllowedByCaller(
             @Nullable Bundle options, int callingUid, @Nullable String callingPackage) {
-        if (options == null || !options.containsKey(
-                        ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED)) {
+        if (options == null) {
             return getDefaultBackgroundStartPrivileges(callingUid, callingPackage);
         }
-        return options.getBoolean(ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED)
-                ? BackgroundStartPrivileges.ALLOW_BAL
-                : BackgroundStartPrivileges.NONE;
+        switch (options.getInt(ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
+                MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED)) {
+            case MODE_BACKGROUND_ACTIVITY_START_DENIED:
+                return BackgroundStartPrivileges.NONE;
+            case MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
+                return getDefaultBackgroundStartPrivileges(callingUid, callingPackage);
+            case MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
+            case MODE_BACKGROUND_ACTIVITY_START_COMPAT:
+            default:
+                return BackgroundStartPrivileges.ALLOW_BAL;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 6779f7a..a5449a0 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -37,6 +37,7 @@
 import static android.system.OsConstants.EAGAIN;
 
 import static com.android.sdksandbox.flags.Flags.selinuxSdkSandboxAudit;
+import static com.android.sdksandbox.flags.Flags.selinuxSdkSandboxInputSelector;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
@@ -2065,11 +2066,15 @@
             }
         }
 
-        return app.info.seInfo
-                + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser) + extraInfo;
+        if (selinuxSdkSandboxInputSelector()) {
+            return app.info.seInfo + extraInfo + TextUtils.emptyIfNull(app.info.seInfoUser);
+        } else {
+            return app.info.seInfo
+                    + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser)
+                    + extraInfo;
+        }
     }
 
-
     @GuardedBy("mService")
     boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
             int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 8eca4fc..2184340 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -690,10 +690,14 @@
         private long mTimeLimitExceededAt = Long.MIN_VALUE;
         @UptimeMillisLong
         private long mTotalRuntime = 0;
+        private int mNumParallelServices = 0;
 
-        TimeLimitedFgsInfo(@UptimeMillisLong long startTime) {
-            mFirstFgsStartUptime = startTime;
-            mFirstFgsStartRealtime = SystemClock.elapsedRealtime();
+        public void noteFgsFgsStart(@UptimeMillisLong long startTime) {
+            mNumParallelServices++;
+            if (mNumParallelServices == 1) {
+                mFirstFgsStartUptime = startTime;
+                mFirstFgsStartRealtime = SystemClock.elapsedRealtime();
+            }
             mLastFgsStartTime = startTime;
         }
 
@@ -707,17 +711,23 @@
             return mFirstFgsStartRealtime;
         }
 
-        public void setLastFgsStartTime(@UptimeMillisLong long startTime) {
-            mLastFgsStartTime = startTime;
-        }
-
         @UptimeMillisLong
         public long getLastFgsStartTime() {
             return mLastFgsStartTime;
         }
 
-        public void updateTotalRuntime() {
-            mTotalRuntime += SystemClock.uptimeMillis() - mLastFgsStartTime;
+        public void decNumParallelServices() {
+            if (mNumParallelServices > 0) {
+                mNumParallelServices--;
+            }
+            if (mNumParallelServices == 0) {
+                mLastFgsStartTime = 0;
+            }
+        }
+
+        public void updateTotalRuntime(@UptimeMillisLong long nowUptime) {
+            mTotalRuntime += nowUptime - mLastFgsStartTime;
+            mLastFgsStartTime = nowUptime;
         }
 
         @UptimeMillisLong
@@ -735,6 +745,7 @@
         }
 
         public void reset() {
+            mNumParallelServices = 0;
             mFirstFgsStartUptime = 0;
             mFirstFgsStartRealtime = 0;
             mLastFgsStartTime = 0;
@@ -1872,8 +1883,8 @@
     /**
      * Called when a time-limited FGS starts.
      */
-    public TimeLimitedFgsInfo createTimeLimitedFgsInfo(@UptimeMillisLong long nowUptime) {
-        return new TimeLimitedFgsInfo(nowUptime);
+    public TimeLimitedFgsInfo createTimeLimitedFgsInfo() {
+        return new TimeLimitedFgsInfo();
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 827db57..5793758 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -29,6 +29,8 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -262,11 +264,11 @@
             Uri settingUri = Settings.Global.getUriFor(globalSetting);
             String propName = makePropertyName(GLOBAL_SETTINGS_CATEGORY, globalSetting);
             if (settingUri == null) {
-                log("setting uri is null for globalSetting " + globalSetting);
+                logErr("setting uri is null for globalSetting " + globalSetting);
                 continue;
             }
             if (propName == null) {
-                log("invalid prop name for globalSetting " + globalSetting);
+                logErr("invalid prop name for globalSetting " + globalSetting);
                 continue;
             }
 
@@ -294,7 +296,7 @@
                         for (String key : properties.getKeyset()) {
                             String propertyName = makePropertyName(scope, key);
                             if (propertyName == null) {
-                                log("unable to construct system property for " + scope + "/"
+                                logErr("unable to construct system property for " + scope + "/"
                                         + key);
                                 return;
                             }
@@ -306,7 +308,7 @@
                             // sys prop slot can be removed.
                             String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key);
                             if (aconfigPropertyName == null) {
-                                log("unable to construct system property for " + scope + "/"
+                                logErr("unable to construct system property for " + scope + "/"
                                         + key);
                                 return;
                             }
@@ -324,7 +326,7 @@
                         for (String key : properties.getKeyset()) {
                             String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key);
                             if (aconfigPropertyName == null) {
-                                log("unable to construct system property for " + scope + "/"
+                                logErr("unable to construct system property for " + scope + "/"
                                         + key);
                                 return;
                             }
@@ -354,7 +356,7 @@
 
                   if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX)
                       || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) {
-                    log("unable to construct system property for " + actualNamespace
+                    logErr("unable to construct system property for " + actualNamespace
                         + "/" + flagName);
                     continue;
                   }
@@ -383,18 +385,18 @@
 
     /**
      * apply flag local override in aconfig new storage
-     * @param props
-     * @return aconfigd socket return
+     * @param requests: request proto output stream
+     * @return aconfigd socket return as proto input stream
      */
-    public static StorageReturnMessages sendAconfigdRequests(StorageRequestMessages requests) {
+    static ProtoInputStream sendAconfigdRequests(ProtoOutputStream requests) {
         // connect to aconfigd socket
         LocalSocket client = new LocalSocket();
         try{
             client.connect(new LocalSocketAddress(
                 "aconfigd", LocalSocketAddress.Namespace.RESERVED));
-            log("connected to aconfigd socket");
+            Slog.d(TAG, "connected to aconfigd socket");
         } catch (IOException ioe) {
-            log("failed to connect to aconfigd socket", ioe);
+            logErr("failed to connect to aconfigd socket", ioe);
             return null;
         }
 
@@ -404,43 +406,93 @@
             inputStream = new DataInputStream(client.getInputStream());
             outputStream = new DataOutputStream(client.getOutputStream());
         } catch (IOException ioe) {
-            log("failed to get local socket iostreams", ioe);
+            logErr("failed to get local socket iostreams", ioe);
             return null;
         }
 
         // send requests
         try {
-            byte[] requests_bytes = requests.toByteArray();
+            byte[] requests_bytes = requests.getBytes();
             outputStream.writeInt(requests_bytes.length);
             outputStream.write(requests_bytes, 0, requests_bytes.length);
-            log(requests.getMsgsCount() + " flag override requests sent to aconfigd");
+            Slog.d(TAG, "flag override requests sent to aconfigd");
         } catch (IOException ioe) {
-            log("failed to send requests to aconfigd", ioe);
+            logErr("failed to send requests to aconfigd", ioe);
             return null;
         }
 
         // read return
-        StorageReturnMessages return_msgs = null;
         try {
             int num_bytes = inputStream.readInt();
-            byte[] buffer = new byte[num_bytes];
-            inputStream.read(buffer, 0, num_bytes);
-            return_msgs = StorageReturnMessages.parseFrom(buffer);
-            log(return_msgs.getMsgsCount() + " acknowledgement received from aconfigd");
+            ProtoInputStream returns = new ProtoInputStream(inputStream);
+            Slog.d(TAG, "received " + num_bytes + " bytes back from aconfigd");
+            return returns;
         } catch (IOException ioe) {
-            log("failed to read requests return from aconfigd", ioe);
+            logErr("failed to read requests return from aconfigd", ioe);
             return null;
         }
+    }
 
-        return return_msgs;
+    /**
+     * serialize a flag override request
+     * @param proto
+     */
+    static void writeFlagOverrideRequest(
+        ProtoOutputStream proto, String packageName, String flagName, String flagValue,
+        boolean isLocal) {
+      long msgsToken = proto.start(StorageRequestMessages.MSGS);
+      long msgToken = proto.start(StorageRequestMessage.FLAG_OVERRIDE_MESSAGE);
+      proto.write(StorageRequestMessage.FlagOverrideMessage.PACKAGE_NAME, packageName);
+      proto.write(StorageRequestMessage.FlagOverrideMessage.FLAG_NAME, flagName);
+      proto.write(StorageRequestMessage.FlagOverrideMessage.FLAG_VALUE, flagValue);
+      proto.write(StorageRequestMessage.FlagOverrideMessage.IS_LOCAL, isLocal);
+      proto.end(msgToken);
+      proto.end(msgsToken);
+    }
+
+    /**
+     * deserialize a flag input proto stream and log
+     * @param proto
+     */
+    static void parseAndLogAconfigdReturn(ProtoInputStream proto) throws IOException {
+        while (true) {
+          switch (proto.nextField()) {
+            case (int) StorageReturnMessages.MSGS:
+              long msgsToken = proto.start(StorageReturnMessages.MSGS);
+              switch (proto.nextField()) {
+                case (int) StorageReturnMessage.FLAG_OVERRIDE_MESSAGE:
+                  Slog.d(TAG, "successfully handled override requests");
+                  long msgToken = proto.start(StorageReturnMessage.FLAG_OVERRIDE_MESSAGE);
+                  proto.end(msgToken);
+                  break;
+                case (int) StorageReturnMessage.ERROR_MESSAGE:
+                  String errmsg = proto.readString(StorageReturnMessage.ERROR_MESSAGE);
+                  Slog.d(TAG, "override request failed: " + errmsg);
+                  break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                  break;
+                default:
+                  logErr("invalid message type, expecting only flag override return or error message");
+                  break;
+              }
+              proto.end(msgsToken);
+              break;
+            case ProtoInputStream.NO_MORE_FIELDS:
+              return;
+            default:
+              logErr("invalid message type, expect storage return message");
+              break;
+          }
+        }
     }
 
     /**
      * apply flag local override in aconfig new storage
      * @param props
      */
-    public static void setLocalOverridesInNewStorage(DeviceConfig.Properties props) {
-        StorageRequestMessages.Builder requests_builder = StorageRequestMessages.newBuilder();
+    static void setLocalOverridesInNewStorage(DeviceConfig.Properties props) {
+        int num_requests = 0;
+        ProtoOutputStream requests = new ProtoOutputStream();
         for (String flagName : props.getKeyset()) {
             String flagValue = props.getString(flagName, null);
             if (flagName == null || flagValue == null) {
@@ -449,32 +501,35 @@
 
             int idx = flagName.indexOf(":");
             if (idx == -1 || idx == flagName.length() - 1 || idx == 0) {
-                log("invalid local flag override: " + flagName);
+                logErr("invalid local flag override: " + flagName);
                 continue;
             }
             String actualNamespace = flagName.substring(0, idx);
             String fullFlagName = flagName.substring(idx+1);
             idx = fullFlagName.lastIndexOf(".");
             if (idx == -1) {
-              log("invalid flag name: " + fullFlagName);
+              logErr("invalid flag name: " + fullFlagName);
               continue;
             }
             String packageName = fullFlagName.substring(0, idx);
             String realFlagName = fullFlagName.substring(idx+1);
-
-            StorageRequestMessage.FlagOverrideMessage.Builder override_msg_builder =
-                StorageRequestMessage.FlagOverrideMessage.newBuilder();
-            override_msg_builder.setPackageName(packageName);
-            override_msg_builder.setFlagName(realFlagName);
-            override_msg_builder.setFlagValue(flagValue);
-            override_msg_builder.setIsLocal(true);
-
-            StorageRequestMessage.Builder request_builder = StorageRequestMessage.newBuilder();
-            request_builder.setFlagOverrideMessage(override_msg_builder.build());
-            requests_builder.addMsgs(request_builder.build());
+            writeFlagOverrideRequest(requests, packageName, realFlagName, flagValue, true);
+            ++num_requests;
         }
-        StorageRequestMessages requests = requests_builder.build();
-        StorageReturnMessages acks = sendAconfigdRequests(requests);
+
+        if (num_requests == 0) {
+          return;
+        }
+
+        // send requests to aconfigd and obtain the return byte buffer
+        ProtoInputStream returns = sendAconfigdRequests(requests);
+
+        // deserialize back using proto input stream
+        try {
+          parseAndLogAconfigdReturn(returns);
+        } catch (IOException ioe) {
+            logErr("failed to parse aconfigd return", ioe);
+        }
     }
 
     public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
@@ -517,7 +572,7 @@
         for (String property_name : property_names) {
             String[] segments = property_name.split("\\.");
             if (segments.length < 3) {
-                log("failed to extract category name from property " + property_name);
+                logErr("failed to extract category name from property " + property_name);
                 continue;
             }
             categories.add(segments[2]);
@@ -545,14 +600,16 @@
         return propertyName;
     }
 
+
     /**
      * stage flags in aconfig new storage
      * @param propsToStage
      */
     @VisibleForTesting
     static void stageFlagsInNewStorage(HashMap<String, HashMap<String, String>> propsToStage) {
-        // create storage request proto
-        StorageRequestMessages.Builder requests_builder = StorageRequestMessages.newBuilder();
+        // write aconfigd requests proto to proto output stream
+        int num_requests = 0;
+        ProtoOutputStream requests = new ProtoOutputStream();
         for (HashMap.Entry<String, HashMap<String, String>> entry : propsToStage.entrySet()) {
             String actualNamespace = entry.getKey();
             HashMap<String, String> flagValuesToStage = entry.getValue();
@@ -560,26 +617,29 @@
                 String stagedValue = flagValuesToStage.get(fullFlagName);
                 int idx = fullFlagName.lastIndexOf(".");
                 if (idx == -1) {
-                    log("invalid flag name: " + fullFlagName);
+                    logErr("invalid flag name: " + fullFlagName);
                     continue;
                 }
                 String packageName = fullFlagName.substring(0, idx);
                 String flagName = fullFlagName.substring(idx+1);
-
-                StorageRequestMessage.FlagOverrideMessage.Builder override_msg_builder =
-                    StorageRequestMessage.FlagOverrideMessage.newBuilder();
-                override_msg_builder.setPackageName(packageName);
-                override_msg_builder.setFlagName(flagName);
-                override_msg_builder.setFlagValue(stagedValue);
-                override_msg_builder.setIsLocal(false);
-
-                StorageRequestMessage.Builder request_builder = StorageRequestMessage.newBuilder();
-                request_builder.setFlagOverrideMessage(override_msg_builder.build());
-                requests_builder.addMsgs(request_builder.build());
+                writeFlagOverrideRequest(requests, packageName, flagName, stagedValue, false);
+                ++num_requests;
             }
         }
-        StorageRequestMessages requests = requests_builder.build();
-        StorageReturnMessages acks = sendAconfigdRequests(requests);
+
+        if (num_requests == 0) {
+          return;
+        }
+
+        // send requests to aconfigd and obtain the return
+        ProtoInputStream returns = sendAconfigdRequests(requests);
+
+        // deserialize back using proto input stream
+        try {
+          parseAndLogAconfigdReturn(returns);
+        } catch (IOException ioe) {
+            logErr("failed to parse aconfigd return", ioe);
+        }
     }
 
     /**
@@ -620,7 +680,7 @@
       for (String flagName : properties.getKeyset()) {
         int idx = flagName.indexOf(NAMESPACE_REBOOT_STAGING_DELIMITER);
         if (idx == -1 || idx == flagName.length() - 1 || idx == 0) {
-          log("invalid staged flag: " + flagName);
+          logErr("invalid staged flag: " + flagName);
           continue;
         }
         String actualNamespace = flagName.substring(0, idx);
@@ -671,7 +731,7 @@
             }
             value = "";
         } else if (value.length() > SYSTEM_PROPERTY_MAX_LENGTH) {
-            log("key=" + key + " value=" + value + " exceeds system property max length.");
+            logErr("key=" + key + " value=" + value + " exceeds system property max length.");
             return;
         }
 
@@ -681,11 +741,11 @@
             // Failure to set a property can be caused by SELinux denial. This usually indicates
             // that the property wasn't allowlisted in sepolicy.
             // No need to report it on all user devices, only on debug builds.
-            log("Unable to set property " + key + " value '" + value + "'", e);
+            logErr("Unable to set property " + key + " value '" + value + "'", e);
         }
     }
 
-    private static void log(String msg, Exception e) {
+    private static void logErr(String msg, Exception e) {
         if (Build.IS_DEBUGGABLE) {
             Slog.wtf(TAG, msg, e);
         } else {
@@ -693,7 +753,7 @@
         }
     }
 
-    private static void log(String msg) {
+    private static void logErr(String msg) {
         if (Build.IS_DEBUGGABLE) {
             Slog.wtf(TAG, msg);
         } else {
@@ -711,7 +771,7 @@
 
             br.close();
         } catch (IOException ioe) {
-            log("failed to read file " + RESET_RECORD_FILE_PATH, ioe);
+            logErr("failed to read file " + RESET_RECORD_FILE_PATH, ioe);
         }
         return content;
     }
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index fb63ec6..b7108df 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -116,3 +116,10 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "skip_unimportant_connections"
+    namespace: "backstage_power"
+    description: "Avoid OomAdjuster calculations for connections that won't change importance"
+    bug: "323376416"
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 1db3483..ad93f6f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -55,6 +55,7 @@
 import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
 import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM_OPS;
 import static android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE;
+import static android.app.AppOpsManager.UID_STATE_NONEXISTENT;
 import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES;
 import static android.app.AppOpsManager._NUM_OP;
 import static android.app.AppOpsManager.extractFlagsFromKey;
@@ -70,7 +71,6 @@
 import static android.content.Intent.EXTRA_REPLACING;
 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
 import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
-import static android.permission.flags.Flags.runtimePermissionAppopsMappingEnabled;
 
 import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
 
@@ -130,6 +130,7 @@
 import android.os.UserHandle;
 import android.os.storage.StorageManagerInternal;
 import android.permission.PermissionManager;
+import android.permission.flags.Flags;
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -140,6 +141,7 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
+import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
@@ -153,7 +155,6 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IAppOpsStartedCallback;
 import com.android.internal.app.MessageSamplingConfig;
-import com.android.internal.camera.flags.Flags;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.os.Clock;
 import com.android.internal.pm.pkg.component.ParsedAttribution;
@@ -1421,6 +1422,9 @@
     // The callback method from AppOpsUidStateTracker
     private void onUidStateChanged(int uid, int state, boolean foregroundModeMayChange) {
         synchronized (this) {
+            if (state == UID_STATE_NONEXISTENT) {
+                onUidProcessDeathLocked(uid);
+            }
             UidState uidState = getUidStateLocked(uid, false);
 
             boolean hasForegroundWatchers = false;
@@ -1508,6 +1512,11 @@
                 }
             }
 
+            if (state == UID_STATE_NONEXISTENT) {
+                // For UID_STATE_NONEXISTENT, we don't call onUidStateChanged for AttributedOps
+                return;
+            }
+
             if (uidState != null) {
                 int numPkgs = uidState.pkgOps.size();
                 for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
@@ -1532,6 +1541,81 @@
         }
     }
 
+    @GuardedBy("this")
+    private void onUidProcessDeathLocked(int uid) {
+        if (!mUidStates.contains(uid) || !Flags.finishRunningOpsForKilledPackages()) {
+            return;
+        }
+        final SparseLongArray chainsToFinish = new SparseLongArray();
+        doForAllAttributedOpsInUidLocked(uid, (attributedOp) -> {
+            attributedOp.doForAllInProgressStartOpEvents((event) -> {
+                int chainId = event.getAttributionChainId();
+                if (chainId != ATTRIBUTION_CHAIN_ID_NONE) {
+                    long currentEarliestStartTime =
+                            chainsToFinish.get(chainId, Long.MAX_VALUE);
+                    if (event.getStartTime() < currentEarliestStartTime) {
+                        // Store the earliest chain link we're finishing, so that we can go back
+                        // and finish any links in the chain that started after this one
+                        chainsToFinish.put(chainId, event.getStartTime());
+                    }
+                }
+                attributedOp.finished(event.getClientId());
+            });
+        });
+        finishChainsLocked(chainsToFinish);
+    }
+
+    @GuardedBy("this")
+    private void finishChainsLocked(SparseLongArray chainsToFinish) {
+        doForAllAttributedOpsLocked((attributedOp) -> {
+            attributedOp.doForAllInProgressStartOpEvents((event) -> {
+                int chainId = event.getAttributionChainId();
+                // If this event is part of a chain, and this event started after the event in the
+                // chain we already finished, then finish this event, too
+                long earliestEventStart = chainsToFinish.get(chainId, Long.MAX_VALUE);
+                if (chainId != ATTRIBUTION_CHAIN_ID_NONE
+                        && event.getStartTime() >= earliestEventStart) {
+                    attributedOp.finished(event.getClientId());
+                }
+            });
+        });
+    }
+
+    @GuardedBy("this")
+    private void doForAllAttributedOpsLocked(Consumer<AttributedOp> action) {
+        int numUids = mUidStates.size();
+        for (int uidNum = 0; uidNum < numUids; uidNum++) {
+            int uid = mUidStates.keyAt(uidNum);
+            doForAllAttributedOpsInUidLocked(uid, action);
+        }
+    }
+
+    @GuardedBy("this")
+    private void doForAllAttributedOpsInUidLocked(int uid, Consumer<AttributedOp> action) {
+        UidState uidState = mUidStates.get(uid);
+        if (uidState == null) {
+            return;
+        }
+
+        int numPkgs = uidState.pkgOps.size();
+        for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
+            Ops ops = uidState.pkgOps.valueAt(pkgNum);
+            int numOps = ops.size();
+            for (int opNum = 0; opNum < numOps; opNum++) {
+                Op op = ops.valueAt(opNum);
+                int numDevices = op.mDeviceAttributedOps.size();
+                for (int deviceNum = 0; deviceNum < numDevices; deviceNum++) {
+                    ArrayMap<String, AttributedOp> attrOps =
+                            op.mDeviceAttributedOps.valueAt(deviceNum);
+                    int numAttributions = attrOps.size();
+                    for (int attrNum = 0; attrNum < numAttributions; attrNum++) {
+                        action.accept(attrOps.valueAt(attrNum));
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Notify the proc state or capability has changed for a certain UID.
      */
@@ -2702,7 +2786,7 @@
      * have information on them.
      */
     private static boolean isOpAllowedForUid(int uid) {
-        return runtimePermissionAppopsMappingEnabled()
+        return Flags.runtimePermissionAppopsMappingEnabled()
                 && (uid == Process.ROOT_UID || uid == Process.SYSTEM_UID);
     }
 
@@ -4775,8 +4859,8 @@
         if ((code == OP_CAMERA) && isAutomotive()) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                if ((Flags.cameraPrivacyAllowlist())
-                        && (mSensorPrivacyManager.isCameraPrivacyEnabled(packageName))) {
+                if (com.android.internal.camera.flags.Flags.cameraPrivacyAllowlist()
+                        && mSensorPrivacyManager.isCameraPrivacyEnabled(packageName)) {
                     return true;
                 }
             } finally {
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
index 18ea8cf..268b286 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
@@ -68,6 +68,7 @@
             return UID_STATE_BACKGROUND;
         }
 
+        // UID_STATE_NONEXISTENT is deliberately excluded here
         return UID_STATE_CACHED;
     }
 
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index bc6ef20..03c8156 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -34,7 +34,9 @@
 import static android.app.AppOpsManager.OP_TAKE_AUDIO_FOCUS;
 import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
 import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
+import static android.app.AppOpsManager.UID_STATE_NONEXISTENT;
 import static android.app.AppOpsManager.UID_STATE_TOP;
+import static android.permission.flags.Flags.finishRunningOpsForKilledPackages;
 
 import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
 
@@ -343,13 +345,14 @@
         int capability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
         boolean appWidgetVisible = mAppWidgetVisible.get(uid, false);
 
+        boolean foregroundChange = uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+                != pendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+                || capability != pendingCapability
+                || appWidgetVisible != pendingAppWidgetVisible;
+
         if (uidState != pendingUidState
                 || capability != pendingCapability
                 || appWidgetVisible != pendingAppWidgetVisible) {
-            boolean foregroundChange = uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
-                    != pendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
-                    || capability != pendingCapability
-                    || appWidgetVisible != pendingAppWidgetVisible;
 
             if (foregroundChange) {
                 // To save on memory usage, log only interesting changes.
@@ -372,6 +375,16 @@
             mCapability.delete(uid);
             mAppWidgetVisible.delete(uid);
             mPendingGone.delete(uid);
+            if (finishRunningOpsForKilledPackages()) {
+                for (int i = 0; i < mUidStateChangedCallbacks.size(); i++) {
+                    UidStateChangedCallback cb = mUidStateChangedCallbacks.keyAt(i);
+                    Executor executor = mUidStateChangedCallbacks.valueAt(i);
+
+                    executor.execute(PooledLambda.obtainRunnable(
+                            UidStateChangedCallback::onUidStateChanged, cb, uid,
+                            UID_STATE_NONEXISTENT, foregroundChange));
+                }
+            }
         } else {
             mUidStates.put(uid, pendingUidState);
             mCapability.put(uid, pendingCapability);
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 2760ccf..02fc993 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -38,6 +38,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.function.Consumer;
 
 final class AttributedOp {
     private final @NonNull AppOpsService mAppOpsService;
@@ -256,6 +257,19 @@
         }
     }
 
+    public void doForAllInProgressStartOpEvents(Consumer<InProgressStartOpEvent> action) {
+        ArrayMap<IBinder, AttributedOp.InProgressStartOpEvent> events = isPaused()
+                ? mPausedInProgressEvents : mInProgressEvents;
+        if (events == null) {
+            return;
+        }
+
+        int numStartedOps = events.size();
+        for (int i = 0; i < numStartedOps; i++) {
+            action.accept(events.valueAt(i));
+        }
+    }
+
     /**
      * Update state when finishOp was called. Will finish started ops, and delete paused ops.
      *
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c310822..15c5c10 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4440,7 +4440,8 @@
                     || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING) {
                 voiceActive = true;
             }
-            if (usage == AudioAttributes.USAGE_MEDIA || usage == AudioAttributes.USAGE_GAME) {
+            if (usage == AudioAttributes.USAGE_MEDIA || usage == AudioAttributes.USAGE_GAME
+                    || usage == AudioAttributes.USAGE_UNKNOWN) {
                 mediaActive = true;
             }
         }
diff --git a/services/core/java/com/android/server/audio/MusicFxHelper.java b/services/core/java/com/android/server/audio/MusicFxHelper.java
index ba45310..cf0b2ae 100644
--- a/services/core/java/com/android/server/audio/MusicFxHelper.java
+++ b/services/core/java/com/android/server/audio/MusicFxHelper.java
@@ -70,6 +70,8 @@
     // The binder token identifying the UidObserver registration.
     private IBinder mUidObserverToken = null;
 
+    private boolean mIsBound;
+
     // Package name and list of open audio sessions for this package
     private static class PackageSessions {
         String mPackageName;
@@ -109,6 +111,7 @@
                 if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
                     Intent bindIntent = new Intent().setClassName(mMusicFxPackageName,
                             "com.android.musicfx.KeepAliveService");
+                    mIsBound = true;
                     mContext.bindServiceAsUser(
                             bindIntent, mMusicFxBindConnection, Context.BIND_AUTO_CREATE,
                             UserHandle.of(getCurrentUserId()));
@@ -157,9 +160,12 @@
                     Log.e(TAG, "RemoteException with unregisterUidObserver: " + e);
                 }
                 mUidObserverToken = null;
-                mContext.unbindService(mMusicFxBindConnection);
-                Log.i(TAG, "last session closed, unregister UID observer, and unbind "
-                        + mMusicFxPackageName);
+                if (mIsBound) {
+                    mContext.unbindService(mMusicFxBindConnection);
+                    mIsBound = false;
+                    Log.i(TAG, "last session closed, unregister UID observer, and unbind "
+                            + mMusicFxPackageName);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index e2c4b46..cae1695 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -347,9 +347,6 @@
     //------------------------------------------------------
     // routing monitoring
     synchronized void onRoutingUpdated() {
-        if (!mFeatureEnabled) {
-            return;
-        }
         switch (mState) {
             case STATE_UNINITIALIZED:
             case STATE_NOT_SUPPORTED:
@@ -393,7 +390,7 @@
             setDispatchAvailableState(false);
         }
 
-        boolean enabled = able && enabledAvailable.first;
+        boolean enabled = mFeatureEnabled && able && enabledAvailable.first;
         if (enabled) {
             loglogi("Enabling Spatial Audio since enabled for media device:"
                     + currentDevice);
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 11cca66..2a16872 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -298,7 +298,7 @@
                 return -1;
             }
 
-            if (promptInfo.containsTestConfigurations()) {
+            if (promptInfo.requiresTestOrInternalPermission()) {
                 if (getContext().checkCallingOrSelfPermission(TEST_BIOMETRIC)
                         != PackageManager.PERMISSION_GRANTED) {
                     checkInternalPermission();
@@ -306,10 +306,10 @@
             }
 
             // Only allow internal clients to enable non-public options.
-            if (promptInfo.containsPrivateApiConfigurations()) {
+            if (promptInfo.requiresInternalPermission()) {
                 checkInternalPermission();
             }
-            if (promptInfo.containsAdvancedApiConfigurations()) {
+            if (promptInfo.requiresAdvancedPermission()) {
                 checkBiometricAdvancedPermission();
             }
 
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index d9c3ab8..30d12e6 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -1189,7 +1189,11 @@
         update();
     }
 
-    void switchMode(@AutomaticBrightnessMode int mode) {
+    /**
+     * Responsible for switching the AutomaticBrightnessMode of the associated display. Also takes
+     * care of resetting the short term model wherever required
+     */
+    public void switchMode(@AutomaticBrightnessMode int mode) {
         if (!mBrightnessMappingStrategyMap.contains(mode)) {
             return;
         }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 70a1014..0fcdf19 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -84,6 +84,7 @@
 import com.android.server.display.brightness.DisplayBrightnessController;
 import com.android.server.display.brightness.clamper.BrightnessClamperController;
 import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy2;
+import com.android.server.display.brightness.strategy.DisplayBrightnessStrategyConstants;
 import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
 import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
 import com.android.server.display.config.HysteresisLevels;
@@ -797,7 +798,7 @@
                 return;
             }
             mDisplayStateController.overrideDozeScreenState(displayState, reason);
-            sendUpdatePowerState();
+            updatePowerState();
         }, mClock.uptimeMillis());
     }
 
@@ -1333,12 +1334,6 @@
                 mDisplayStateController.shouldPerformScreenOffTransition());
         state = mPowerState.getScreenState();
 
-        // Switch to doze auto-brightness mode if needed
-        if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
-                && !mAutomaticBrightnessController.isInIdleMode()) {
-            mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
-                    ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
-        }
 
         DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController
                 .updateBrightness(mPowerRequest, state);
@@ -1372,6 +1367,13 @@
         final boolean wasShortTermModelActive =
                 mAutomaticBrightnessStrategy.isShortTermModelActive();
         if (!mFlags.isRefactorDisplayPowerControllerEnabled()) {
+            // Switch to doze auto-brightness mode if needed
+            if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
+                    && !mAutomaticBrightnessController.isInIdleMode()) {
+                mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
+                        ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
+            }
+
             mAutomaticBrightnessStrategy.setAutoBrightnessState(state,
                     mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(),
                     mBrightnessReasonTemp.getReason(), mPowerRequest.policy,
@@ -1440,45 +1442,52 @@
             brightnessState = clampScreenBrightness(brightnessState);
         }
 
-        // If there's an offload session, we need to set the initial doze brightness before
-        // the offload session starts controlling the brightness.
-        // During the transition DOZE_SUSPEND -> DOZE -> DOZE_SUSPEND, this brightness strategy
-        // will be selected again, meaning that no new brightness will be sent to the hardware and
-        // the display will stay at the brightness level set by the offload session.
-        if (Float.isNaN(brightnessState) && mFlags.isDisplayOffloadEnabled()
-                && Display.isDozeState(state) && mDisplayOffloadSession != null) {
-            if (mAutomaticBrightnessController != null
-                    && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
-                // Use the auto-brightness curve and the last observed lux
-                rawBrightnessState = mAutomaticBrightnessController
-                        .getAutomaticScreenBrightnessBasedOnLastUsedLux(
-                                mTempBrightnessEvent);
-            } else {
-                rawBrightnessState = getDozeBrightnessForOffload();
-                mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
-                        | BrightnessEvent.FLAG_DOZE_SCALE);
-            }
-
-            if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) {
-                brightnessState = clampScreenBrightness(rawBrightnessState);
-                mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL);
-
+        if (Display.isDozeState(state)) {
+            // If there's an offload session, we need to set the initial doze brightness before
+            // the offload session starts controlling the brightness.
+            // During the transition DOZE_SUSPEND -> DOZE -> DOZE_SUSPEND, this brightness strategy
+            // will be selected again, meaning that no new brightness will be sent to the hardware
+            // and the display will stay at the brightness level set by the offload session.
+            if ((Float.isNaN(brightnessState)
+                    || displayBrightnessState.getDisplayBrightnessStrategyName()
+                    .equals(DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME))
+                    && mFlags.isDisplayOffloadEnabled()
+                    && mDisplayOffloadSession != null) {
                 if (mAutomaticBrightnessController != null
                         && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
-                    // Keep the brightness in the setting so that we can use it after the screen
-                    // turns on, until a lux sample becomes available. We don't do this when
-                    // auto-brightness is disabled - in that situation we still want to use
-                    // the last brightness from when the screen was on.
-                    updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState;
+                    // Use the auto-brightness curve and the last observed lux
+                    rawBrightnessState = mAutomaticBrightnessController
+                            .getAutomaticScreenBrightnessBasedOnLastUsedLux(
+                                    mTempBrightnessEvent);
+                } else {
+                    rawBrightnessState = getDozeBrightnessForOffload();
+                    mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
+                            | BrightnessEvent.FLAG_DOZE_SCALE);
+                }
+
+                if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) {
+                    brightnessState = clampScreenBrightness(rawBrightnessState);
+                    mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL);
+
+                    if (mAutomaticBrightnessController != null
+                            && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
+                        // Keep the brightness in the setting so that we can use it after the screen
+                        // turns on, until a lux sample becomes available. We don't do this when
+                        // auto-brightness is disabled - in that situation we still want to use
+                        // the last brightness from when the screen was on.
+                        updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState;
+                    }
                 }
             }
-        }
 
-        // Use default brightness when dozing unless overridden.
-        if (Float.isNaN(brightnessState) && Display.isDozeState(state)) {
-            rawBrightnessState = mScreenBrightnessDozeConfig;
-            brightnessState = clampScreenBrightness(rawBrightnessState);
-            mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
+            // Use default brightness when dozing unless overridden.
+            if (Float.isNaN(brightnessState)
+                    || displayBrightnessState.getDisplayBrightnessStrategyName()
+                    .equals(DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME)) {
+                rawBrightnessState = mScreenBrightnessDozeConfig;
+                brightnessState = clampScreenBrightness(rawBrightnessState);
+                mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
+            }
         }
 
         if (!mFlags.isRefactorDisplayPowerControllerEnabled()) {
@@ -1502,7 +1511,7 @@
         }
 
         // Apply manual brightness.
-        if (Float.isNaN(brightnessState)) {
+        if (Float.isNaN(brightnessState) && !mFlags.isRefactorDisplayPowerControllerEnabled()) {
             rawBrightnessState = currentBrightnessSetting;
             brightnessState = clampScreenBrightness(rawBrightnessState);
             if (brightnessState != currentBrightnessSetting) {
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 22a21a6..feec4e6 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -32,6 +32,7 @@
 import com.android.server.display.brightness.strategy.BoostBrightnessStrategy;
 import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
 import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
+import com.android.server.display.brightness.strategy.FallbackBrightnessStrategy;
 import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
 import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
 import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
@@ -85,6 +86,9 @@
     @Nullable
     private final AutoBrightnessFallbackStrategy mAutoBrightnessFallbackStrategy;
 
+    @Nullable
+    private final FallbackBrightnessStrategy mFallbackBrightnessStrategy;
+
     // A collective representation of all the strategies that the selector is aware of. This is
     // non null, but the strategies this is tracking can be null
     @NonNull
@@ -118,7 +122,8 @@
         mInvalidBrightnessStrategy = injector.getInvalidBrightnessStrategy();
         mAutomaticBrightnessStrategy1 =
                 (!mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) ? null
-                        : injector.getAutomaticBrightnessStrategy1(context, displayId);
+                        : injector.getAutomaticBrightnessStrategy1(context, displayId,
+                                mDisplayManagerFlags);
         mAutomaticBrightnessStrategy2 =
                 (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) ? null
                         : injector.getAutomaticBrightnessStrategy2(context, displayId);
@@ -134,11 +139,14 @@
         } else {
             mOffloadBrightnessStrategy = null;
         }
+        mFallbackBrightnessStrategy = (mDisplayManagerFlags
+                .isRefactorDisplayPowerControllerEnabled())
+                ? injector.getFallbackBrightnessStrategy() : null;
         mDisplayBrightnessStrategies = new DisplayBrightnessStrategy[]{mInvalidBrightnessStrategy,
                 mScreenOffBrightnessStrategy, mDozeBrightnessStrategy, mFollowerBrightnessStrategy,
                 mBoostBrightnessStrategy, mOverrideBrightnessStrategy, mTemporaryBrightnessStrategy,
                 mAutomaticBrightnessStrategy1, mOffloadBrightnessStrategy,
-                mAutoBrightnessFallbackStrategy};
+                mAutoBrightnessFallbackStrategy, mFallbackBrightnessStrategy};
         mAllowAutoBrightnessWhileDozingConfig = context.getResources().getBoolean(
                 R.bool.config_allowAutoBrightnessWhileDozing);
         mOldBrightnessStrategyName = mInvalidBrightnessStrategy.getName();
@@ -179,6 +187,12 @@
             displayBrightnessStrategy = mOffloadBrightnessStrategy;
         } else if (isAutoBrightnessFallbackStrategyValid()) {
             displayBrightnessStrategy = mAutoBrightnessFallbackStrategy;
+        } else {
+            // This will become the ultimate fallback strategy once the flag has been fully rolled
+            // out
+            if (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) {
+                displayBrightnessStrategy = mFallbackBrightnessStrategy;
+            }
         }
 
         if (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) {
@@ -330,8 +344,8 @@
         }
 
         AutomaticBrightnessStrategy getAutomaticBrightnessStrategy1(Context context,
-                int displayId) {
-            return new AutomaticBrightnessStrategy(context, displayId);
+                int displayId, DisplayManagerFlags displayManagerFlags) {
+            return new AutomaticBrightnessStrategy(context, displayId, displayManagerFlags);
         }
 
         AutomaticBrightnessStrategy2 getAutomaticBrightnessStrategy2(Context context,
@@ -347,5 +361,9 @@
         AutoBrightnessFallbackStrategy getAutoBrightnessFallbackStrategy() {
             return new AutoBrightnessFallbackStrategy(/* injector= */ null);
         }
+
+        FallbackBrightnessStrategy getFallbackBrightnessStrategy() {
+            return new FallbackBrightnessStrategy();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 2305228..f809a49 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -17,6 +17,9 @@
 
 import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
 
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.display.BrightnessConfiguration;
@@ -33,6 +36,7 @@
 import com.android.server.display.brightness.BrightnessUtils;
 import com.android.server.display.brightness.StrategyExecutionRequest;
 import com.android.server.display.brightness.StrategySelectionNotifyRequest;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import java.io.PrintWriter;
 
@@ -98,19 +102,24 @@
 
     private Injector mInjector;
 
+    private DisplayManagerFlags mDisplayManagerFlags;
+
     @VisibleForTesting
-    AutomaticBrightnessStrategy(Context context, int displayId, Injector injector) {
+    AutomaticBrightnessStrategy(Context context, int displayId, Injector injector,
+            DisplayManagerFlags displayManagerFlags) {
         super(context, displayId);
         mContext = context;
         mDisplayId = displayId;
         mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
         mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        mDisplayManagerFlags  = displayManagerFlags;
         mInjector = (injector == null) ? new RealInjector() : injector;
     }
 
-    public AutomaticBrightnessStrategy(Context context, int displayId) {
-        this(context, displayId, null);
+    public AutomaticBrightnessStrategy(Context context, int displayId,
+            DisplayManagerFlags displayManagerFlags) {
+        this(context, displayId, null, displayManagerFlags);
     }
 
     /**
@@ -120,6 +129,7 @@
     public void setAutoBrightnessState(int targetDisplayState,
             boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
             float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
+        switchMode(targetDisplayState);
         final boolean autoBrightnessEnabledInDoze =
                 allowAutoBrightnessWhileDozingConfig && policy == POLICY_DOZE;
         mIsAutoBrightnessEnabled = shouldUseAutoBrightness()
@@ -479,6 +489,16 @@
         }
     }
 
+
+    private void switchMode(int state) {
+        if (mDisplayManagerFlags.areAutoBrightnessModesEnabled()
+                && mAutomaticBrightnessController != null
+                && !mAutomaticBrightnessController.isInIdleMode()) {
+            mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
+                    ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
+        }
+    }
+
     /**
      * Evaluates if there are any temporary auto-brightness adjustments which is not applied yet.
      * Temporary brightness adjustments happen when the user moves the brightness slider in the
diff --git a/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java
index 504683a..7b2f2b9 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java
@@ -18,4 +18,5 @@
 
 public class DisplayBrightnessStrategyConstants {
     static final String INVALID_BRIGHTNESS_STRATEGY_NAME = "InvalidBrightnessStrategy";
+    public static final String FALLBACK_BRIGHTNESS_STRATEGY_NAME = "FallbackBrightnessStrategy";
 }
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
new file mode 100644
index 0000000..3463649a
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+import android.annotation.NonNull;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.StrategyExecutionRequest;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages the brightness of the associated display when no other strategy qualifies for
+ * setting up the brightness state. This strategy is also being used for evaluating the
+ * display brightness state when we have a manually set brightness. This is a temporary state, and
+ * the logic for evaluating the manual brightness will be moved to a separate strategy
+ */
+public class FallbackBrightnessStrategy implements DisplayBrightnessStrategy{
+    @Override
+    public DisplayBrightnessState updateBrightness(
+            StrategyExecutionRequest strategyExecutionRequest) {
+        BrightnessReason brightnessReason = new BrightnessReason();
+        brightnessReason.setReason(BrightnessReason.REASON_MANUAL);
+        return new DisplayBrightnessState.Builder()
+                .setBrightness(strategyExecutionRequest.getCurrentScreenBrightness())
+                .setSdrBrightness(strategyExecutionRequest.getCurrentScreenBrightness())
+                .setBrightnessReason(brightnessReason)
+                .setDisplayBrightnessStrategyName(getName())
+                // The fallback brightness might change due to clamping. Make sure we tell the rest
+                // of the system by updating the setting
+                .setShouldUpdateScreenBrightnessSetting(true)
+                .build();
+    }
+
+    @NonNull
+    @Override
+    public String getName() {
+        return DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME;
+    }
+
+    @Override
+    public int getReason() {
+        return BrightnessReason.REASON_MANUAL;
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+
+    }
+
+    @Override
+    public void strategySelectionPostProcessor(
+            StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+
+    }
+}
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 0bb93a9..3883604 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -78,6 +78,7 @@
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.accessibility.Flags;
 import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
@@ -356,6 +357,11 @@
                             case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
                                 onAccessibilityDaltonizerChanged();
                                 break;
+                            case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL:
+                                if (Flags.enableColorCorrectionSaturation()) {
+                                    onAccessibilityDaltonizerChanged();
+                                }
+                                break;
                             case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
                                 updateDisplayWhiteBalanceStatus();
                                 break;
@@ -398,6 +404,11 @@
                 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
         cr.registerContentObserver(Secure.getUriFor(Secure.REDUCE_BRIGHT_COLORS_LEVEL),
                 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        if (Flags.enableColorCorrectionSaturation()) {
+            cr.registerContentObserver(
+                    Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL),
+                    false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        }
 
         // Apply the accessibility settings first, since they override most other settings.
         onAccessibilityInversionChanged();
@@ -597,21 +608,31 @@
         if (mCurrentUser == UserHandle.USER_NULL) {
             return;
         }
+        var contentResolver = getContext().getContentResolver();
         final int daltonizerMode = isAccessiblityDaltonizerEnabled()
-                ? Secure.getIntForUser(getContext().getContentResolver(),
-                    Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
-                    AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
+                ? Secure.getIntForUser(contentResolver,
+                Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
+                AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
                 : AccessibilityManager.DALTONIZER_DISABLED;
 
+        int saturation = NOT_SET;
+        if (Flags.enableColorCorrectionSaturation()) {
+            saturation = Secure.getIntForUser(
+                    contentResolver,
+                    Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
+                    NOT_SET,
+                    mCurrentUser);
+        }
+
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
         if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
             // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
             dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
                     MATRIX_GRAYSCALE);
-            dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
+            dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED, saturation);
         } else {
             dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
-            dtm.setDaltonizerMode(daltonizerMode);
+            dtm.setDaltonizerMode(daltonizerMode, saturation);
         }
     }
 
diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
index 0dba9e1..a76c427 100644
--- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display.color;
 
+import android.annotation.IntRange;
 import android.app.ActivityTaskManager;
 import android.hardware.display.ColorDisplayManager;
 import android.opengl.Matrix;
@@ -111,9 +112,15 @@
     /**
      * Lock used for synchronize access to {@link #mDaltonizerMode}.
      */
-    private final Object mDaltonizerModeLock = new Object();
+    @VisibleForTesting
+    final Object mDaltonizerModeLock = new Object();
+    @VisibleForTesting
     @GuardedBy("mDaltonizerModeLock")
-    private int mDaltonizerMode = -1;
+    int mDaltonizerMode = -1;
+
+    @VisibleForTesting
+    @GuardedBy("mDaltonizerModeLock")
+    int mDaltonizerLevel = -1;
 
     private static final IBinder sFlinger = ServiceManager.getService(SURFACE_FLINGER);
 
@@ -168,12 +175,15 @@
      * various types of color blindness.
      *
      * @param mode the new Daltonization mode, or -1 to disable
+     * @param level the level of saturation for color correction [-1,10] inclusive. -1 for when
+     *              it is not set.
      */
-    public void setDaltonizerMode(int mode) {
+    public void setDaltonizerMode(int mode, @IntRange(from = -1, to = 10) int level) {
         synchronized (mDaltonizerModeLock) {
-            if (mDaltonizerMode != mode) {
+            if (mDaltonizerMode != mode || mDaltonizerLevel != level) {
                 mDaltonizerMode = mode;
-                applyDaltonizerMode(mode);
+                mDaltonizerLevel = level;
+                applyDaltonizerMode(mode, level);
             }
         }
     }
@@ -223,10 +233,11 @@
     /**
      * Propagates the provided Daltonization mode to the SurfaceFlinger.
      */
-    private static void applyDaltonizerMode(int mode) {
+    private static void applyDaltonizerMode(int mode, int level) {
         final Parcel data = Parcel.obtain();
         data.writeInterfaceToken("android.ui.ISurfaceComposer");
         data.writeInt(mode);
+        data.writeInt(level);
         try {
             sFlinger.transact(SURFACE_FLINGER_TRANSACTION_DALTONIZER, data, null, 0);
         } catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/display/config/RefreshRateData.java b/services/core/java/com/android/server/display/config/RefreshRateData.java
index b186fd5..d7ed904 100644
--- a/services/core/java/com/android/server/display/config/RefreshRateData.java
+++ b/services/core/java/com/android/server/display/config/RefreshRateData.java
@@ -20,6 +20,10 @@
 import android.content.res.Resources;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Collections;
+import java.util.List;
 
 /**
  * RefreshRates config for display
@@ -58,12 +62,17 @@
      */
     public final int defaultRefreshRateInHbmSunlight;
 
+    public final List<SupportedModeData> lowPowerSupportedModes;
+
+    @VisibleForTesting
     public RefreshRateData(int defaultRefreshRate, int defaultPeakRefreshRate,
-            int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight) {
+            int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight,
+            List<SupportedModeData> lowPowerSupportedModes) {
         this.defaultRefreshRate = defaultRefreshRate;
         this.defaultPeakRefreshRate = defaultPeakRefreshRate;
         this.defaultRefreshRateInHbmHdr = defaultRefreshRateInHbmHdr;
         this.defaultRefreshRateInHbmSunlight = defaultRefreshRateInHbmSunlight;
+        this.lowPowerSupportedModes = Collections.unmodifiableList(lowPowerSupportedModes);
     }
 
 
@@ -71,9 +80,10 @@
     public String toString() {
         return "RefreshRateData {"
                 + "defaultRefreshRate: " + defaultRefreshRate
-                + "defaultPeakRefreshRate: " + defaultPeakRefreshRate
-                + "defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr
-                + "defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight
+                + ", defaultPeakRefreshRate: " + defaultPeakRefreshRate
+                + ", defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr
+                + ", defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight
+                + ", lowPowerSupportedModes=" + lowPowerSupportedModes
                 + "} ";
     }
 
@@ -90,8 +100,13 @@
         int defaultRefreshRateInHbmSunlight = loadDefaultRefreshRateInHbmSunlight(
                 refreshRateConfigs, resources);
 
+        NonNegativeFloatToFloatMap modes =
+                refreshRateConfigs == null ? null : refreshRateConfigs.getLowPowerSupportedModes();
+        List<SupportedModeData> lowPowerSupportedModes = SupportedModeData.load(modes);
+
         return new RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate,
-                defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight);
+                defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight,
+                lowPowerSupportedModes);
     }
 
     private static int loadDefaultRefreshRate(
diff --git a/services/core/java/com/android/server/display/config/SensorData.java b/services/core/java/com/android/server/display/config/SensorData.java
index 6ad13c3..8bfc4a3 100644
--- a/services/core/java/com/android/server/display/config/SensorData.java
+++ b/services/core/java/com/android/server/display/config/SensorData.java
@@ -24,7 +24,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.display.feature.DisplayManagerFlags;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -42,7 +41,7 @@
     public final String name;
     public final float minRefreshRate;
     public final float maxRefreshRate;
-    public final List<SupportedMode> supportedModes;
+    public final List<SupportedModeData> supportedModes;
 
     @VisibleForTesting
     public SensorData() {
@@ -61,7 +60,7 @@
 
     @VisibleForTesting
     public SensorData(String type, String name, float minRefreshRate, float maxRefreshRate,
-            List<SupportedMode> supportedModes) {
+            List<SupportedModeData> supportedModes) {
         this.type = type;
         this.name = name;
         this.minRefreshRate = minRefreshRate;
@@ -214,26 +213,11 @@
             minRefreshRate = rr.getMinimum().floatValue();
             maxRefreshRate = rr.getMaximum().floatValue();
         }
-        ArrayList<SupportedMode> supportedModes = new ArrayList<>();
-        NonNegativeFloatToFloatMap configSupportedModes = sensorDetails.getSupportedModes();
-        if (configSupportedModes != null) {
-            for (NonNegativeFloatToFloatPoint supportedMode : configSupportedModes.getPoint()) {
-                supportedModes.add(new SupportedMode(supportedMode.getFirst().floatValue(),
-                        supportedMode.getSecond().floatValue()));
-            }
-        }
+        List<SupportedModeData> supportedModes = SupportedModeData.load(
+                sensorDetails.getSupportedModes());
 
         return new SensorData(sensorDetails.getType(), sensorDetails.getName(), minRefreshRate,
                 maxRefreshRate, supportedModes);
     }
 
-    public static class SupportedMode {
-        public final float refreshRate;
-        public final float vsyncRate;
-
-        public SupportedMode(float refreshRate, float vsyncRate) {
-            this.refreshRate = refreshRate;
-            this.vsyncRate = vsyncRate;
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/display/config/SupportedModeData.java b/services/core/java/com/android/server/display/config/SupportedModeData.java
new file mode 100644
index 0000000..3c82884
--- /dev/null
+++ b/services/core/java/com/android/server/display/config/SupportedModeData.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.config;
+
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Supported display mode data. Display mode is uniquely identified by refreshRate-vsync pair
+ */
+public class SupportedModeData {
+    public final float refreshRate;
+    public final float vsyncRate;
+
+    public SupportedModeData(float refreshRate, float vsyncRate) {
+        this.refreshRate = refreshRate;
+        this.vsyncRate = vsyncRate;
+    }
+
+    @Override
+    public String toString() {
+        return "SupportedModeData{"
+                + "refreshRate= " + refreshRate
+                + ", vsyncRate= " + vsyncRate
+                + '}';
+    }
+
+    static List<SupportedModeData> load(@Nullable NonNegativeFloatToFloatMap configMap) {
+        ArrayList<SupportedModeData> supportedModes = new ArrayList<>();
+        if (configMap != null) {
+            for (NonNegativeFloatToFloatPoint supportedMode : configMap.getPoint()) {
+                supportedModes.add(new SupportedModeData(supportedMode.getFirst().floatValue(),
+                        supportedMode.getSecond().floatValue()));
+            }
+        }
+        return supportedModes;
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 91bd80e..846ee23 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -78,6 +78,7 @@
 import com.android.server.display.DisplayDeviceConfig;
 import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
 import com.android.server.display.config.RefreshRateData;
+import com.android.server.display.config.SupportedModeData;
 import com.android.server.display.feature.DeviceConfigParameterProvider;
 import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.utils.AmbientFilter;
@@ -451,15 +452,6 @@
         return config != null && config.isVrrSupportEnabled();
     }
 
-    private boolean isVrrSupportedByAnyDisplayLocked() {
-        for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) {
-            if (mDisplayDeviceConfigByDisplay.valueAt(i).isVrrSupportEnabled()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     /**
      * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate ranges.
      */
@@ -939,18 +931,44 @@
         private final Uri mMatchContentFrameRateSetting =
                 Settings.Secure.getUriFor(Settings.Secure.MATCH_CONTENT_FRAME_RATE);
 
-        private final boolean mVsynLowPowerVoteEnabled;
+        private final boolean mVsyncLowPowerVoteEnabled;
         private final boolean mPeakRefreshRatePhysicalLimitEnabled;
 
         private final Context mContext;
+        private final Handler mHandler;
         private float mDefaultPeakRefreshRate;
         private float mDefaultRefreshRate;
+        private boolean mIsLowPower = false;
+
+        private final DisplayManager.DisplayListener mDisplayListener =
+                new DisplayManager.DisplayListener() {
+                    @Override
+                    public void onDisplayAdded(int displayId) {
+                        synchronized (mLock) {
+                            updateLowPowerModeAllowedModesLocked();
+                        }
+                    }
+
+                    @Override
+                    public void onDisplayRemoved(int displayId) {
+                        mVotesStorage.updateVote(displayId, Vote.PRIORITY_LOW_POWER_MODE_MODES,
+                                null);
+                    }
+
+                    @Override
+                    public void onDisplayChanged(int displayId) {
+                        synchronized (mLock) {
+                            updateLowPowerModeAllowedModesLocked();
+                        }
+                    }
+                };
 
         SettingsObserver(@NonNull Context context, @NonNull Handler handler,
                 DisplayManagerFlags flags) {
             super(handler);
             mContext = context;
-            mVsynLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled();
+            mHandler = handler;
+            mVsyncLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled();
             mPeakRefreshRatePhysicalLimitEnabled = flags.isPeakRefreshRatePhysicalLimitEnabled();
             // We don't want to load from the DeviceConfig while constructing since this leads to
             // a spike in the latency of DisplayManagerService startup. This happens because
@@ -983,6 +1001,7 @@
                     UserHandle.USER_SYSTEM);
             cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
                     this);
+            mInjector.registerDisplayListener(mDisplayListener, mHandler);
 
             float deviceConfigDefaultPeakRefresh =
                     mConfigParameterProvider.getPeakRefreshRateDefault();
@@ -995,6 +1014,7 @@
                 updateLowPowerModeSettingLocked();
                 updateModeSwitchingTypeSettingLocked();
             }
+
         }
 
         public void setDefaultRefreshRate(float refreshRate) {
@@ -1061,23 +1081,36 @@
         }
 
         private void updateLowPowerModeSettingLocked() {
-            boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
+            mIsLowPower = Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
             final Vote vote;
-            if (inLowPowerMode && mVsynLowPowerVoteEnabled && isVrrSupportedByAnyDisplayLocked()) {
-                vote = Vote.forSupportedRefreshRates(List.of(
-                        new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
-                                /* vsyncRate= */ 240f),
-                        new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
-                                /* vsyncRate= */ 60f)
-                ));
-            } else if (inLowPowerMode) {
+            if (mIsLowPower) {
                 vote = Vote.forRenderFrameRates(0f, 60f);
             } else {
                 vote = null;
             }
-            mVotesStorage.updateGlobalVote(Vote.PRIORITY_LOW_POWER_MODE, vote);
-            mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
+            mVotesStorage.updateGlobalVote(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, vote);
+            mBrightnessObserver.onLowPowerModeEnabledLocked(mIsLowPower);
+            updateLowPowerModeAllowedModesLocked();
+        }
+
+        private void updateLowPowerModeAllowedModesLocked() {
+            if (!mVsyncLowPowerVoteEnabled) {
+                return;
+            }
+            if (mIsLowPower) {
+                for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) {
+                    DisplayDeviceConfig config = mDisplayDeviceConfigByDisplay.valueAt(i);
+                    List<SupportedModeData> supportedModes = config
+                            .getRefreshRateData().lowPowerSupportedModes;
+                    mVotesStorage.updateVote(
+                            mDisplayDeviceConfigByDisplay.keyAt(i),
+                            Vote.PRIORITY_LOW_POWER_MODE_MODES,
+                            Vote.forSupportedRefreshRates(supportedModes));
+                }
+            } else {
+                mVotesStorage.removeAllVotesForPriority(Vote.PRIORITY_LOW_POWER_MODE_MODES);
+            }
         }
 
         /**
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index ddb334e..8167c1f 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -18,6 +18,9 @@
 
 import android.annotation.NonNull;
 
+import com.android.server.display.config.SupportedModeData;
+
+import java.util.ArrayList;
 import java.util.List;
 
 interface Vote {
@@ -102,9 +105,15 @@
     // For internal application to limit display modes to specific ids
     int PRIORITY_SYSTEM_REQUESTED_MODES = 14;
 
-    // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
+    // PRIORITY_LOW_POWER_MODE_MODES limits display modes to specific refreshRate-vsync pairs if
     // Settings.Global.LOW_POWER_MODE is on.
-    int PRIORITY_LOW_POWER_MODE = 15;
+    // Lower priority that PRIORITY_LOW_POWER_MODE_RENDER_RATE and if discarded (due to other
+    // higher priority votes), render rate limit can still apply
+    int PRIORITY_LOW_POWER_MODE_MODES = 14;
+
+    // PRIORITY_LOW_POWER_MODE_RENDER_RATE force the render frame rate to [0, 60HZ] if
+    // Settings.Global.LOW_POWER_MODE is on.
+    int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 15;
 
     // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
     // higher priority voters' result is a range, it will fix the rate to a single choice.
@@ -177,22 +186,26 @@
         return new BaseModeRefreshRateVote(baseModeRefreshRate);
     }
 
-    static Vote forSupportedRefreshRates(
-            List<SupportedRefreshRatesVote.RefreshRates> refreshRates) {
-        return new SupportedRefreshRatesVote(refreshRates);
+    static Vote forSupportedRefreshRates(List<SupportedModeData> supportedModes) {
+        if (supportedModes.isEmpty()) {
+            return null;
+        }
+        List<SupportedRefreshRatesVote.RefreshRates> rates = new ArrayList<>();
+        for (SupportedModeData data : supportedModes) {
+            rates.add(new SupportedRefreshRatesVote.RefreshRates(data.refreshRate, data.vsyncRate));
+        }
+        return new SupportedRefreshRatesVote(rates);
     }
 
     static Vote forSupportedModes(List<Integer> modeIds) {
         return new SupportedModesVote(modeIds);
     }
 
-
-
     static Vote forSupportedRefreshRatesAndDisableSwitching(
             List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates) {
         return new CombinedVote(
                 List.of(forDisableRefreshRateSwitching(),
-                        forSupportedRefreshRates(supportedRefreshRates)));
+                        new SupportedRefreshRatesVote(supportedRefreshRates)));
     }
 
     static String priorityToString(int priority) {
@@ -213,8 +226,10 @@
                 return "PRIORITY_HIGH_BRIGHTNESS_MODE";
             case PRIORITY_PROXIMITY:
                 return "PRIORITY_PROXIMITY";
-            case PRIORITY_LOW_POWER_MODE:
-                return "PRIORITY_LOW_POWER_MODE";
+            case PRIORITY_LOW_POWER_MODE_MODES:
+                return "PRIORITY_LOW_POWER_MODE_MODES";
+            case PRIORITY_LOW_POWER_MODE_RENDER_RATE:
+                return "PRIORITY_LOW_POWER_MODE_RENDER_RATE";
             case PRIORITY_SKIN_TEMPERATURE:
                 return "PRIORITY_SKIN_TEMPERATURE";
             case PRIORITY_UDFPS:
@@ -227,6 +242,8 @@
                 return "PRIORITY_LIMIT_MODE";
             case PRIORITY_SYNCHRONIZED_REFRESH_RATE:
                 return "PRIORITY_SYNCHRONIZED_REFRESH_RATE";
+            case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
+                return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
             case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
                 return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
             case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 816242d..c6aef7f 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -249,14 +249,14 @@
         mCurrentDream.mAppTask = appTask;
     }
 
-    void setDreamHasFocus(boolean hasFocus) {
+    void setDreamIsObscured(boolean isObscured) {
         if (mCurrentDream != null) {
-            mCurrentDream.mDreamHasFocus = hasFocus;
+            mCurrentDream.mDreamIsObscured = isObscured;
         }
     }
 
-    boolean dreamHasFocus() {
-        return mCurrentDream != null && mCurrentDream.mDreamHasFocus;
+    boolean dreamIsFrontmost() {
+        return mCurrentDream != null && mCurrentDream.dreamIsFrontmost();
     }
 
     /**
@@ -451,7 +451,7 @@
         private String mStopReason;
         private long mDreamStartTime;
         public boolean mWakingGently;
-        public boolean mDreamHasFocus;
+        private boolean mDreamIsObscured;
 
         private final Runnable mStopPreviousDreamsIfNeeded = this::stopPreviousDreamsIfNeeded;
         private final Runnable mReleaseWakeLockIfNeeded = this::releaseWakeLockIfNeeded;
@@ -549,5 +549,9 @@
                 mHandler.removeCallbacks(mReleaseWakeLockIfNeeded);
             }
         }
+
+        boolean dreamIsFrontmost() {
+            return !mDreamIsObscured;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 2def5ae..18a9986 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -20,7 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.service.dreams.Flags.dreamTracksFocus;
+import static android.service.dreams.Flags.dreamHandlesBeingObscured;
 
 import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID;
 
@@ -428,7 +428,7 @@
             // Can't start dreaming if we are already dreaming and the dream has focus. If we are
             // dreaming but the dream does not have focus, then the dream can be brought to the
             // front so it does have focus.
-            if (isScreenOn && isDreamingInternal() && dreamHasFocus()) {
+            if (isScreenOn && isDreamingInternal() && dreamIsFrontmost()) {
                 return false;
             }
 
@@ -463,9 +463,10 @@
         }
     }
 
-    private boolean dreamHasFocus() {
-        // Dreams always had focus before they were able to track it.
-        return !dreamTracksFocus() || mController.dreamHasFocus();
+    private boolean dreamIsFrontmost() {
+        // Dreams were always considered frontmost before they began tracking whether they are
+        // obscured.
+        return !dreamHandlesBeingObscured() || mController.dreamIsFrontmost();
     }
 
     protected void requestStartDreamFromShell() {
@@ -473,7 +474,7 @@
     }
 
     private void requestDreamInternal() {
-        if (isDreamingInternal() && !dreamHasFocus() && mController.bringDreamToFront()) {
+        if (isDreamingInternal() && !dreamIsFrontmost() && mController.bringDreamToFront()) {
             return;
         }
 
@@ -1159,10 +1160,16 @@
         }
 
         @Override
-        public void onDreamFocusChanged(boolean hasFocus) {
+        public void setDreamIsObscured(boolean isObscured) {
+            if (!dreamHandlesBeingObscured()) {
+                return;
+            }
+
+            checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
             final long ident = Binder.clearCallingIdentity();
             try {
-                mController.setDreamHasFocus(hasFocus);
+                mHandler.post(() -> mController.setDreamIsObscured(isObscured));
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d21fc85..5db17bb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -29,7 +29,6 @@
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.sysprop.HdmiProperties;
 import android.util.Slog;
 
@@ -278,8 +277,7 @@
     void dismissUiOnActiveSourceStatusRecovered() {
         assertRunOnServiceThread();
         Intent intent = new Intent(HdmiControlManager.ACTION_ON_ACTIVE_SOURCE_RECOVERED_DISMISS_UI);
-        mService.getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
-                HdmiControlService.PERMISSION);
+        mService.sendBroadcastAsUser(intent);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 46061a5..275c930 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -206,6 +206,10 @@
         launchDeviceDiscovery();
         startQueuedActions();
         if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
+            if (hasAction(RequestActiveSourceAction.class)) {
+                Slog.i(TAG, "RequestActiveSourceAction is in progress. Restarting.");
+                removeAction(RequestActiveSourceAction.class);
+            }
             addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
                 @Override
                 public void onComplete(int result) {
@@ -1308,6 +1312,8 @@
                 mService.sendCecCommand(
                         HdmiCecMessageBuilder.buildActiveSource(
                                 getDeviceInfo().getLogicalAddress(), activePath));
+                updateActiveSource(getDeviceInfo().getLogicalAddress(), activePath,
+                        "HdmiCecLocalDeviceTv#launchRoutingControl()");
             }
         }
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index d2d0279..cca73b5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -40,6 +40,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -1645,6 +1646,13 @@
             case Constants.MESSAGE_ROUTING_CHANGE:
             case Constants.MESSAGE_SET_STREAM_PATH:
             case Constants.MESSAGE_TEXT_VIEW_ON:
+                // RequestActiveSourceAction is started after the TV finished logical address
+                // allocation. This action is used by the TV to get the active source from the CEC
+                // network. If the TV sent a source changing CEC message, this action does not have
+                // to continue anymore.
+                if (isTvDeviceEnabled()) {
+                    tv().removeAction(RequestActiveSourceAction.class);
+                }
                 sendCecCommandWithRetries(command, callback);
                 break;
             default:
@@ -4392,8 +4400,7 @@
         assertRunOnServiceThread();
         Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
         intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
-        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
-                HdmiControlService.PERMISSION);
+        sendBroadcastAsUser(intent);
     }
 
     @ServiceThreadOnly
@@ -4402,8 +4409,17 @@
         Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
         intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
         intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRA_PARAM1, extra);
-        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
-                HdmiControlService.PERMISSION);
+        sendBroadcastAsUser(intent);
+    }
+
+    // This method is used such that we can override it inside unit tests to avoid a
+    // SecurityException.
+    @ServiceThreadOnly
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+        assertRunOnServiceThread();
+        getContext().sendBroadcastAsUser(intent, UserHandle.ALL, HdmiControlService.PERMISSION);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
index d250416..539a00d 100644
--- a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
@@ -21,13 +21,20 @@
 import android.util.Slog;
 
 /**
- * Feature action that sends <Request Active Source> message and waits for <Active Source>.
+ * Feature action that sends <Request Active Source> message and waits for <Active Source> on TV
+ * panels.
+ * This action has a delay before sending <Request Active Source>. This is because it should wait
+ * for a possible request from LauncherX and can be cancelled if an <Active Source> message was
+ * received or the TV switched to another input.
  */
 public class RequestActiveSourceAction extends HdmiCecFeatureAction {
     private static final String TAG = "RequestActiveSourceAction";
 
+    // State to wait for the LauncherX to call the CEC API.
+    private static final int STATE_WAIT_FOR_LAUNCHERX_API_CALL = 1;
+
     // State to wait for the <Active Source> message.
-    private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 1;
+    private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 2;
 
     // Number of retries <Request Active Source> is sent if no device answers this message.
     private static final int MAX_SEND_RETRY_COUNT = 1;
@@ -43,10 +50,12 @@
     boolean start() {
         Slog.v(TAG, "RequestActiveSourceAction started.");
 
-        sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
+        mState = STATE_WAIT_FOR_LAUNCHERX_API_CALL;
 
-        mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
-        addTimer(mState, HdmiConfig.TIMEOUT_MS);
+        // We wait for default timeout to allow the message triggered by the LauncherX API call to
+        // be sent by the TV and another default timeout in case the message has to be answered
+        // (e.g. TV sent a <Set Stream Path> or <Routing Change>).
+        addTimer(mState, HdmiConfig.TIMEOUT_MS * 2);
         return true;
     }
 
@@ -65,13 +74,23 @@
         if (mState != state) {
             return;
         }
-        if (mState == STATE_WAIT_FOR_ACTIVE_SOURCE) {
-            if (mSendRetryCount++ < MAX_SEND_RETRY_COUNT) {
+
+        switch (mState) {
+            case STATE_WAIT_FOR_LAUNCHERX_API_CALL:
+                mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
                 sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
                 addTimer(mState, HdmiConfig.TIMEOUT_MS);
-            } else {
-                finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
-            }
+                return;
+            case STATE_WAIT_FOR_ACTIVE_SOURCE:
+                if (mSendRetryCount++ < MAX_SEND_RETRY_COUNT) {
+                    sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
+                    addTimer(mState, HdmiConfig.TIMEOUT_MS);
+                } else {
+                    finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
+                }
+                return;
+            default:
+                return;
         }
     }
 }
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index b47631c3..d32a5ed 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -218,4 +218,13 @@
      * display, external peripherals, fingerprint sensor, etc.
      */
     public abstract void notifyUserActivity();
+
+    /**
+     * Get the device ID of the {@link InputDevice} that used most recently.
+     *
+     * @return the last used input device ID, or
+     *     {@link android.os.IInputConstants#INVALID_INPUT_DEVICE_ID} if no device has been used
+     *     since boot.
+     */
+    public abstract int getLastUsedInputDeviceId();
 }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 8317991..8e85b81 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2671,24 +2671,6 @@
         return null;
     }
 
-    private static class PointerDisplayIdChangedArgs {
-        final int mPointerDisplayId;
-        final float mXPosition;
-        final float mYPosition;
-        PointerDisplayIdChangedArgs(int pointerDisplayId, float xPosition, float yPosition) {
-            mPointerDisplayId = pointerDisplayId;
-            mXPosition = xPosition;
-            mYPosition = yPosition;
-        }
-    }
-
-    // Native callback.
-    @SuppressWarnings("unused")
-    @VisibleForTesting
-    void onPointerDisplayIdChanged(int pointerDisplayId, float xPosition, float yPosition) {
-        // TODO(b/311416205): Remove.
-    }
-
     @Override
     @EnforcePermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
     public void registerStickyModifierStateListener(
@@ -3204,6 +3186,11 @@
         public void setStylusButtonMotionEventsEnabled(boolean enabled) {
             mNative.setStylusButtonMotionEventsEnabled(enabled);
         }
+
+        @Override
+        public int getLastUsedInputDeviceId() {
+            return mNative.getLastUsedInputDeviceId();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index f742360..0208a32 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -271,6 +271,15 @@
 
     void setInputMethodConnectionIsActive(boolean isActive);
 
+    /**
+     * Get the device ID of the InputDevice that used most recently.
+     *
+     * @return the last used input device ID, or
+     *     {@link android.os.IInputConstants#INVALID_INPUT_DEVICE_ID} if no device has been used
+     *     since boot.
+     */
+    int getLastUsedInputDeviceId();
+
     /** The native implementation of InputManagerService methods. */
     class NativeImpl implements NativeInputManagerService {
         /** Pointer to native input manager service object, used by native code. */
@@ -544,5 +553,8 @@
 
         @Override
         public native void setInputMethodConnectionIsActive(boolean isActive);
+
+        @Override
+        public native int getLastUsedInputDeviceId();
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
index 00bc751..ad98b4a 100644
--- a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
+++ b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
@@ -29,6 +29,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
 import com.android.internal.inputmethod.IInlineSuggestionsResponseCallback;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
 import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
 
 /**
@@ -49,12 +50,12 @@
 
     private static final class CreateInlineSuggestionsRequest {
         @NonNull final InlineSuggestionsRequestInfo mRequestInfo;
-        @NonNull final IInlineSuggestionsRequestCallback mCallback;
+        @NonNull final InlineSuggestionsRequestCallback mCallback;
         @NonNull final String mPackageName;
 
         CreateInlineSuggestionsRequest(
                 @NonNull InlineSuggestionsRequestInfo requestInfo,
-                @NonNull IInlineSuggestionsRequestCallback callback,
+                @NonNull InlineSuggestionsRequestCallback callback,
                 @NonNull String packageName) {
             mRequestInfo = requestInfo;
             mCallback = callback;
@@ -78,7 +79,7 @@
      */
     @GuardedBy("ImfLock.class")
     @Nullable
-    private IInlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback;
+    private InlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback;
 
     AutofillSuggestionsController(@NonNull InputMethodManagerService service) {
         mService = service;
@@ -97,33 +98,30 @@
 
     @GuardedBy("ImfLock.class")
     void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
-            InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback,
+            InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback callback,
             boolean touchExplorationEnabled) {
         clearPendingInlineSuggestionsRequest();
         mInlineSuggestionsRequestCallback = callback;
         final InputMethodInfo imi = mService.queryInputMethodForCurrentUserLocked(
                 mService.getSelectedMethodIdLocked());
-        try {
-            if (userId == mService.getCurrentImeUserIdLocked()
-                    && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
-                mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest(
-                        requestInfo, callback, imi.getPackageName());
-                if (mService.getCurMethodLocked() != null) {
-                    // In the normal case when the IME is connected, we can make the request here.
-                    performOnCreateInlineSuggestionsRequest();
-                } else {
-                    // Otherwise, the next time the IME connection is established,
-                    // InputMethodBindingController.mMainConnection#onServiceConnected() will call
-                    // into #performOnCreateInlineSuggestionsRequestLocked() to make the request.
-                    if (DEBUG) {
-                        Slog.d(TAG, "IME not connected. Delaying inline suggestions request.");
-                    }
-                }
+
+        if (userId == mService.getCurrentImeUserIdLocked()
+                && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
+            mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest(
+                    requestInfo, callback, imi.getPackageName());
+            if (mService.getCurMethodLocked() != null) {
+                // In the normal case when the IME is connected, we can make the request here.
+                performOnCreateInlineSuggestionsRequest();
             } else {
-                callback.onInlineSuggestionsUnsupported();
+                // Otherwise, the next time the IME connection is established,
+                // InputMethodBindingController.mMainConnection#onServiceConnected() will call
+                // into #performOnCreateInlineSuggestionsRequestLocked() to make the request.
+                if (DEBUG) {
+                    Slog.d(TAG, "IME not connected. Delaying inline suggestions request.");
+                }
             }
-        } catch (RemoteException e) {
-            Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
+        } else {
+            callback.onInlineSuggestionsUnsupported();
         }
     }
 
@@ -166,11 +164,7 @@
     @GuardedBy("ImfLock.class")
     void invalidateAutofillSession() {
         if (mInlineSuggestionsRequestCallback != null) {
-            try {
-                mInlineSuggestionsRequestCallback.onInlineSuggestionsSessionInvalidated();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Cannot invalidate autofill session.", e);
-            }
+            mInlineSuggestionsRequestCallback.onInlineSuggestionsSessionInvalidated();
         }
     }
 
@@ -180,13 +174,13 @@
      */
     private final class InlineSuggestionsRequestCallbackDecorator
             extends IInlineSuggestionsRequestCallback.Stub {
-        @NonNull private final IInlineSuggestionsRequestCallback mCallback;
+        @NonNull private final InlineSuggestionsRequestCallback mCallback;
         @NonNull private final String mImePackageName;
         private final int mImeDisplayId;
         @NonNull private final IBinder mImeToken;
 
         InlineSuggestionsRequestCallbackDecorator(
-                @NonNull IInlineSuggestionsRequestCallback callback, @NonNull String imePackageName,
+                @NonNull InlineSuggestionsRequestCallback callback, @NonNull String imePackageName,
                 int displayId, @NonNull IBinder imeToken) {
             mCallback = callback;
             mImePackageName = imePackageName;
@@ -195,7 +189,7 @@
         }
 
         @Override
-        public void onInlineSuggestionsUnsupported() throws RemoteException {
+        public void onInlineSuggestionsUnsupported() {
             mCallback.onInlineSuggestionsUnsupported();
         }
 
@@ -220,32 +214,32 @@
         }
 
         @Override
-        public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
+        public void onInputMethodStartInput(AutofillId imeFieldId) {
             mCallback.onInputMethodStartInput(imeFieldId);
         }
 
         @Override
-        public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
+        public void onInputMethodShowInputRequested(boolean requestResult) {
             mCallback.onInputMethodShowInputRequested(requestResult);
         }
 
         @Override
-        public void onInputMethodStartInputView() throws RemoteException {
+        public void onInputMethodStartInputView() {
             mCallback.onInputMethodStartInputView();
         }
 
         @Override
-        public void onInputMethodFinishInputView() throws RemoteException {
+        public void onInputMethodFinishInputView() {
             mCallback.onInputMethodFinishInputView();
         }
 
         @Override
-        public void onInputMethodFinishInput() throws RemoteException {
+        public void onInputMethodFinishInput() {
             mCallback.onInputMethodFinishInput();
         }
 
         @Override
-        public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
+        public void onInlineSuggestionsSessionInvalidated() {
             mCallback.onInlineSuggestionsSessionInvalidated();
         }
     }
diff --git a/services/core/java/com/android/server/inputmethod/ImeBindingState.java b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
index 4c20c3b..f78ea84 100644
--- a/services/core/java/com/android/server/inputmethod/ImeBindingState.java
+++ b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
@@ -21,7 +21,9 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.util.Printer;
 import android.util.proto.ProtoOutputStream;
 import android.view.WindowManager;
@@ -36,6 +38,9 @@
  */
 final class ImeBindingState {
 
+    @UserIdInt
+    final int mUserId;
+
     /**
      * The last window token that we confirmed to be focused.  This is always updated upon
      * reports from the input method client. If the window state is already changed before the
@@ -90,6 +95,7 @@
 
     static ImeBindingState newEmptyState() {
         return new ImeBindingState(
+                /*userId=*/ UserHandle.USER_NULL,
                 /*focusedWindow=*/ null,
                 /*focusedWindowSoftInputMode=*/ SOFT_INPUT_STATE_UNSPECIFIED,
                 /*focusedWindowClient=*/ null,
@@ -97,10 +103,12 @@
         );
     }
 
-    ImeBindingState(@Nullable IBinder focusedWindow,
+    ImeBindingState(@UserIdInt int userId,
+            @Nullable IBinder focusedWindow,
             @SoftInputModeFlags int focusedWindowSoftInputMode,
             @Nullable ClientState focusedWindowClient,
             @Nullable EditorInfo focusedWindowEditorInfo) {
+        mUserId = userId;
         mFocusedWindow = focusedWindow;
         mFocusedWindowSoftInputMode = focusedWindowSoftInputMode;
         mFocusedWindowClient = focusedWindowClient;
diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
index 1c14fc1..fff0e6e 100644
--- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
+++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
@@ -133,6 +133,13 @@
         }
     }
 
+    @Override
+    public void onDispatched(@NonNull ImeTracker.Token statsToken) {
+        synchronized (mLock) {
+            mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
+        }
+    }
+
     /**
      * Updates the IME request tracking token with new information available in IMMS.
      *
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index b709174..e862c7e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -443,7 +443,16 @@
             mCurId = info.getId();
             mLastBindTime = SystemClock.uptimeMillis();
 
-            addFreshWindowToken();
+            final int displayIdToShowIme = mService.getDisplayIdToShowImeLocked();
+            mCurToken = new Binder();
+            mService.setCurTokenDisplayIdLocked(displayIdToShowIme);
+            if (DEBUG) {
+                Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
+                        + displayIdToShowIme);
+            }
+            mWindowManagerInternal.addWindowToken(mCurToken,
+                    WindowManager.LayoutParams.TYPE_INPUT_METHOD,
+                    displayIdToShowIme, null /* options */);
             return new InputBindResult(
                     InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
                     null, null, null, mCurId, mCurSeq, false);
@@ -471,22 +480,6 @@
     }
 
     @GuardedBy("ImfLock.class")
-    private void addFreshWindowToken() {
-        int displayIdToShowIme = mService.getDisplayIdToShowImeLocked();
-        mCurToken = new Binder();
-
-        mService.setCurTokenDisplayIdLocked(displayIdToShowIme);
-
-        if (DEBUG) {
-            Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
-                    + displayIdToShowIme);
-        }
-        mWindowManagerInternal.addWindowToken(mCurToken,
-                WindowManager.LayoutParams.TYPE_INPUT_METHOD,
-                displayIdToShowIme, null /* options */);
-    }
-
-    @GuardedBy("ImfLock.class")
     private void unbindMainConnection() {
         mContext.unbindService(mMainConnection);
         mHasMainConnection = false;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
index 6339686..458c359 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.os.Parcel;
 import android.text.TextUtils;
 import android.util.Slog;
 import android.view.inputmethod.InputMethodInfo;
@@ -323,4 +324,24 @@
         return SubtypeUtils.containsSubtypeOf(imi, requiredLocale, checkCountry,
                 requiredSubtypeMode);
     }
+
+    /**
+     * Marshals the given {@link InputMethodInfo} into a byte array.
+     *
+     * @param imi {@link InputMethodInfo} to be marshalled
+     * @return a byte array where the given {@link InputMethodInfo} is marshalled
+     */
+    @NonNull
+    static byte[] marshal(@NonNull InputMethodInfo imi) {
+        Parcel parcel = null;
+        try {
+            parcel = Parcel.obtain();
+            parcel.writeTypedObject(imi, 0);
+            return parcel.marshall();
+        } finally {
+            if (parcel != null) {
+                parcel.recycle();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index e8543f2..dace67f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -25,7 +25,7 @@
 import android.view.inputmethod.InputMethodInfo;
 
 import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
-import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
 import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.server.LocalServices;
@@ -86,11 +86,11 @@
      *
      * @param userId      the user ID to be queried
      * @param requestInfo information needed to create an {@link InlineSuggestionsRequest}.
-     * @param cb          {@link IInlineSuggestionsRequestCallback} used to pass back the request
+     * @param cb          {@link InlineSuggestionsRequestCallback} used to pass back the request
      *                    object
      */
     public abstract void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
-            InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb);
+            InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback cb);
 
     /**
      * Force switch to the enabled input method by {@code imeId} for current user. If the input
@@ -263,7 +263,7 @@
                 @Override
                 public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
                         InlineSuggestionsRequestInfo requestInfo,
-                        IInlineSuggestionsRequestCallback cb) {
+                        InlineSuggestionsRequestCallback cb) {
                 }
 
                 @Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 848f74e..f8dda60 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -147,7 +147,6 @@
 import com.android.internal.inputmethod.IBooleanListener;
 import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
 import com.android.internal.inputmethod.IImeTracker;
-import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethod;
 import com.android.internal.inputmethod.IInputMethodClient;
@@ -157,6 +156,7 @@
 import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
 import com.android.internal.inputmethod.IRemoteInputConnection;
 import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
 import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
 import com.android.internal.inputmethod.InputBindResult;
 import com.android.internal.inputmethod.InputMethodDebug;
@@ -262,6 +262,7 @@
     private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
     private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
     private static final String HANDLER_THREAD_NAME = "android.imms";
+    private static final String PACKAGE_MONITOR_THREAD_NAME = "android.imms2";
 
     /**
      * When set, {@link #startInputUncheckedLocked} will return
@@ -281,10 +282,35 @@
     @NonNull
     private final String[] mNonPreemptibleInputMethods;
 
+    /**
+     * See {@link #shouldEnableExperimentalConcurrentMultiUserMode(Context)} about when set to be
+     * {@code true}.
+     */
+    private final boolean mExperimentalConcurrentMultiUserModeEnabled;
+
+    /**
+     * Returns {@code true} if experimental concurrent multi-user mode is enabled.
+     *
+     * <p>Currently not compatible with profiles (e.g. work profile).</p>
+     *
+     * @param context {@link Context} to be used to query
+     *                {@link PackageManager#FEATURE_AUTOMOTIVE}
+     * @return {@code true} if experimental concurrent multi-user mode is enabled.
+     */
+    static boolean shouldEnableExperimentalConcurrentMultiUserMode(@NonNull Context context) {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+                && UserManager.isVisibleBackgroundUsersEnabled()
+                && context.getResources().getBoolean(android.R.bool.config_perDisplayFocusEnabled)
+                && Flags.concurrentInputMethods();
+    }
+
     final Context mContext;
     final Resources mRes;
     private final Handler mHandler;
 
+    @NonNull
+    private final Handler mPackageMonitorHandler;
+
     @MultiUserUnawareField
     @UserIdInt
     @GuardedBy("ImfLock.class")
@@ -485,16 +511,6 @@
         return userData.mBindingController.getSelectedMethodId();
     }
 
-    /**
-     * The current binding sequence number, incremented every time there is
-     * a new bind performed.
-     */
-    @GuardedBy("ImfLock.class")
-    private int getSequenceNumberLocked() {
-        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-        return userData.mBindingController.getSequenceNumber();
-    }
-
     @GuardedBy("ImfLock.class")
     @Nullable
     InputMethodInfo queryInputMethodForCurrentUserLocked(@NonNull String imeId) {
@@ -543,21 +559,6 @@
     EditorInfo mCurEditorInfo;
 
     /**
-     * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
-     * connected to or in the process of connecting to.
-     *
-     * <p>This can be {@code null} when no input method is connected.</p>
-     *
-     * @see #getSelectedMethodIdLocked()
-     */
-    @GuardedBy("ImfLock.class")
-    @Nullable
-    private String getCurIdLocked() {
-        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-        return userData.mBindingController.getCurId();
-    }
-
-    /**
      * The current subtype of the current input method.
      */
     @MultiUserUnawareField
@@ -587,16 +588,6 @@
     boolean mInFullscreenMode;
 
     /**
-     * The Intent used to connect to the current input method.
-     */
-    @GuardedBy("ImfLock.class")
-    @Nullable
-    private Intent getCurIntentLocked() {
-        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-        return userData.mBindingController.getCurIntent();
-    }
-
-    /**
      * The token we have made for the currently active input method, to
      * identify it in the future.
      */
@@ -642,15 +633,6 @@
     }
 
     /**
-     * If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntentLocked()}.
-     */
-    @GuardedBy("ImfLock.class")
-    private int getCurMethodUidLocked() {
-        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-        return userData.mBindingController.getCurMethodUid();
-    }
-
-    /**
      * Have we called mCurMethod.bindInput()?
      */
     @MultiUserUnawareField
@@ -1068,8 +1050,16 @@
         }
 
         private void onFinishPackageChangesInternal() {
+            final int userId = getChangingUserId();
+
+            // Instantiating InputMethodInfo requires disk I/O.
+            // Do them before acquiring the lock to minimize the chances of ANR (b/340221861).
+            final var newMethodMapWithoutAdditionalSubtypes =
+                    queryInputMethodServicesInternal(mContext, userId,
+                            AdditionalSubtypeMap.EMPTY_MAP, DirectBootAwareness.AUTO)
+                            .getMethodMap();
+
             synchronized (ImfLock.class) {
-                final int userId = getChangingUserId();
                 final boolean isCurrentUser = (userId == mCurrentUserId);
                 final AdditionalSubtypeMap additionalSubtypeMap =
                         AdditionalSubtypeMapRepository.get(userId);
@@ -1121,9 +1111,10 @@
                         && !(additionalSubtypeChanged || shouldRebuildInputMethodListLocked())) {
                     return;
                 }
-
-                final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
-                        userId, newAdditionalSubtypeMap, DirectBootAwareness.AUTO);
+                final var newMethodMap = newMethodMapWithoutAdditionalSubtypes
+                        .applyAdditionalSubtypes(newAdditionalSubtypeMap);
+                final InputMethodSettings newSettings =
+                        InputMethodSettings.create(newMethodMap, userId);
                 InputMethodSettingsRepository.put(userId, newSettings);
                 if (!isCurrentUser) {
                     return;
@@ -1222,8 +1213,10 @@
     public static final class Lifecycle extends SystemService {
         private final InputMethodManagerService mService;
 
+
         public Lifecycle(Context context) {
-            this(context, new InputMethodManagerService(context));
+            this(context, new InputMethodManagerService(context,
+                            shouldEnableExperimentalConcurrentMultiUserMode(context)));
         }
 
         public Lifecycle(
@@ -1322,16 +1315,21 @@
         mHandler.post(task);
     }
 
-    public InputMethodManagerService(Context context) {
-        this(context, null, null);
+    public InputMethodManagerService(Context context,
+            boolean experimentalConcurrentMultiUserModeEnabled) {
+        this(context, experimentalConcurrentMultiUserModeEnabled, null, null, null);
     }
 
     @VisibleForTesting
     InputMethodManagerService(
             Context context,
+            boolean experimentalConcurrentMultiUserModeEnabled,
             @Nullable ServiceThread serviceThreadForTesting,
+            @Nullable ServiceThread packageMonitorThreadForTesting,
             @Nullable IntFunction<InputMethodBindingController> bindingControllerForTesting) {
         synchronized (ImfLock.class) {
+            mExperimentalConcurrentMultiUserModeEnabled =
+                    experimentalConcurrentMultiUserModeEnabled;
             mContext = context;
             mRes = context.getResources();
             SecureSettingsWrapper.onStart(mContext);
@@ -1347,6 +1345,17 @@
                                     true /* allowIo */);
             thread.start();
             mHandler = Handler.createAsync(thread.getLooper(), this);
+            {
+                final ServiceThread packageMonitorThread =
+                        packageMonitorThreadForTesting != null
+                                ? packageMonitorThreadForTesting
+                                : new ServiceThread(
+                                        PACKAGE_MONITOR_THREAD_NAME,
+                                        Process.THREAD_PRIORITY_FOREGROUND,
+                                        true /* allowIo */);
+                packageMonitorThread.start();
+                mPackageMonitorHandler = Handler.createAsync(packageMonitorThread.getLooper());
+            }
             SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler);
             mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null
                     ? serviceThreadForTesting.getLooper() : Looper.getMainLooper());
@@ -1522,7 +1531,8 @@
         // Note that in b/197848765 we want to see if we can keep the binding alive for better
         // profile switching.
         final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-        userData.mBindingController.unbindCurrentMethod();
+        final var bindingController = userData.mBindingController;
+        bindingController.unbindCurrentMethod();
 
         unbindCurrentClientLocked(UnbindReason.SWITCH_USER);
 
@@ -1620,7 +1630,7 @@
                     }
                 }, "Lazily initialize IMMS#mImeDrawsImeNavBarRes");
 
-                mMyPackageMonitor.register(mContext, UserHandle.ALL, mHandler);
+                mMyPackageMonitor.register(mContext, UserHandle.ALL, mPackageMonitorHandler);
                 mSettingsObserver.registerContentObserverLocked(currentUserId);
 
                 final IntentFilter broadcastFilterForAllUsers = new IntentFilter();
@@ -1648,8 +1658,9 @@
 
     /**
      * Returns true iff the caller is identified to be the current input method with the token.
-     * @param token The window token given to the input method when it was started.
-     * @return true if and only if non-null valid token is specified.
+     *
+     * @param token the window token given to the input method when it was started
+     * @return true if and only if non-null valid token is specified
      */
     @GuardedBy("ImfLock.class")
     private boolean calledWithValidTokenLocked(@NonNull IBinder token) {
@@ -1741,9 +1752,10 @@
             // Check if selected IME of current user supports handwriting.
             if (userId == mCurrentUserId) {
                 final var userData = mUserDataRepository.getOrCreate(userId);
-                return userData.mBindingController.supportsStylusHandwriting()
+                final var bindingController = userData.mBindingController;
+                return bindingController.supportsStylusHandwriting()
                         && (!connectionless
-                        || userData.mBindingController.supportsConnectionlessStylusHandwriting());
+                        || bindingController.supportsConnectionlessStylusHandwriting());
             }
             final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
             final InputMethodInfo imi = settings.getMethodMap().get(
@@ -1804,10 +1816,11 @@
     /**
      * Gets enabled subtypes of the specified {@link InputMethodInfo}.
      *
-     * @param imiId if null, returns enabled subtypes for the current {@link InputMethodInfo}.
+     * @param imiId                           if null, returns enabled subtypes for the current
+     *                                        {@link InputMethodInfo}
      * @param allowsImplicitlyEnabledSubtypes {@code true} to return the implicitly enabled
-     *                                         subtypes.
-     * @param userId the user ID to be queried about.
+     *                                        subtypes
+     * @param userId                          the user ID to be queried about
      */
     @Override
     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
@@ -1851,10 +1864,11 @@
      * <p>As a general principle, IPCs from the application process that take
      * {@link IInputMethodClient} will be rejected without this step.</p>
      *
-     * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
-     *               of {@link android.view.inputmethod.InputMethodManager} that runs on the client
-     *               process
-     * @param inputConnection communication channel for the fallback {@link InputConnection}
+     * @param client                {@link android.os.Binder} proxy that is associated with the
+     *                              singleton instance of
+     *                              {@link android.view.inputmethod.InputMethodManager} that runs
+     *                              on the client process
+     * @param inputConnection       communication channel for the fallback {@link InputConnection}
      * @param selfReportedDisplayId self-reported display ID to which the client is associated.
      *                              Whether the client is still allowed to access to this display
      *                              or not needs to be evaluated every time the client interacts
@@ -1879,10 +1893,10 @@
         }
     }
 
-    // TODO(b/325515685): Move this method to InputMethodBindingController
     /**
      * Hide the IME if the removed user is the current user.
      */
+    // TODO(b/325515685): Move this method to InputMethodBindingController
     @GuardedBy("ImfLock.class")
     private void onClientRemoved(ClientState client) {
         clearClientSessionLocked(client);
@@ -1915,7 +1929,6 @@
         return mClientController.getClient(client.asBinder());
     }
 
-    // TODO(b/314150112): Move this to ClientController.
     @GuardedBy("ImfLock.class")
     void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
         if (mCurClient != null) {
@@ -1935,7 +1948,14 @@
             // all accessibility too. That means, when input method get disconnected (including
             // switching ime), we also unbind accessibility
             mCurClient.mClient.setActive(false /* active */, false /* fullscreen */);
-            mCurClient.mClient.onUnbindMethod(getSequenceNumberLocked(), unbindClientReason);
+
+            // TODO(b/325515685): make binding controller user independent. Before this change, the
+            //  following dependencies also need to be user independent: mCurClient, mBoundToMethod,
+            //  getCurMethodLocked(), and mMenuController.
+            final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+            final var bindingController = userData.mBindingController;
+            mCurClient.mClient.onUnbindMethod(bindingController.getSequenceNumber(),
+                    unbindClientReason);
             mCurClient.mSessionRequested = false;
             mCurClient.mSessionRequestedForAccessibility = false;
             mCurClient = null;
@@ -2013,12 +2033,15 @@
 
         final boolean restarting = !initial;
         final Binder startInputToken = new Binder();
+        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+        final var bindingController = userData.mBindingController;
         final StartInputInfo info = new StartInputInfo(mCurrentUserId,
                 getCurTokenLocked(),
-                mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting,
-                UserHandle.getUserId(mCurClient.mUid),
+                getCurTokenDisplayIdLocked(), bindingController.getCurId(), startInputReason,
+                restarting, UserHandle.getUserId(mCurClient.mUid),
                 mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo,
-                mImeBindingState.mFocusedWindowSoftInputMode, getSequenceNumberLocked());
+                mImeBindingState.mFocusedWindowSoftInputMode,
+                bindingController.getSequenceNumber());
         mImeTargetWindowMap.put(startInputToken, mImeBindingState.mFocusedWindow);
         mStartInputHistory.addEntry(info);
 
@@ -2029,8 +2052,8 @@
         // INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
         if (mCurrentUserId == UserHandle.getUserId(
                 mCurClient.mUid)) {
-            mPackageManagerInternal.grantImplicitAccess(mCurrentUserId,
-                    null /* intent */, UserHandle.getAppId(getCurMethodUidLocked()),
+            mPackageManagerInternal.grantImplicitAccess(mCurrentUserId, null /* intent */,
+                    UserHandle.getAppId(bindingController.getCurMethodUid()),
                     mCurClient.mUid, true /* direct */);
         }
 
@@ -2051,21 +2074,20 @@
                     null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT);
         }
 
-        String curId = getCurIdLocked();
+        final var curId = bindingController.getCurId();
         final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(mCurrentUserId)
                 .getMethodMap().get(curId);
         final boolean suppressesSpellChecker =
                 curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
         final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions =
                 createAccessibilityInputMethodSessions(mCurClient.mAccessibilitySessions);
-        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-        if (userData.mBindingController.supportsStylusHandwriting() && hasSupportedStylusLocked()) {
+        if (bindingController.supportsStylusHandwriting() && hasSupportedStylusLocked()) {
             mHwController.setInkWindowInitializer(new InkWindowInitializer());
         }
         return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
                 session.mSession, accessibilityInputMethodSessions,
                 (session.mChannel != null ? session.mChannel.dup() : null),
-                curId, getSequenceNumberLocked(), suppressesSpellChecker);
+                curId, bindingController.getSequenceNumber(), suppressesSpellChecker);
     }
 
     @GuardedBy("ImfLock.class")
@@ -2159,7 +2181,8 @@
         final boolean connectionWasActive = mCurInputConnection != null;
 
         // Bump up the sequence for this client and attach it.
-        userData.mBindingController.advanceSequenceNumber();
+        final var bindingController = userData.mBindingController;
+        bindingController.advanceSequenceNumber();
 
         mCurClient = cs;
         mCurInputConnection = inputConnection;
@@ -2182,7 +2205,6 @@
         if (connectionIsActive != connectionWasActive) {
             mInputManagerInternal.notifyInputMethodConnectionActive(connectionIsActive);
         }
-        final var bindingController = userData.mBindingController;
 
         // If configured, we want to avoid starting up the IME if it is not supposed to be showing
         if (shouldPreventImeStartupLocked(selectedMethodId, startInputFlags,
@@ -2200,7 +2222,7 @@
         // display ID.
         final String curId = bindingController.getCurId();
         if (curId != null && curId.equals(bindingController.getSelectedMethodId())
-                && mDisplayIdToShowIme == mCurTokenDisplayId) {
+                && mDisplayIdToShowIme == getCurTokenDisplayIdLocked()) {
             if (cs.mCurSession != null) {
                 // Fast case: if we are already connected to the input method,
                 // then just return it.
@@ -2231,11 +2253,13 @@
 
     /**
      * Update the current deviceId and return the relevant imeId for this device.
-     *   1. If the device changes to virtual and its custom IME is not available, then disable IME.
-     *   2. If the device changes to virtual with valid custom IME, then return the custom IME. If
-     *      the old device was default, then store the current imeId so it can be restored.
-     *   3. If the device changes to default, restore the default device IME.
-     *   4. Otherwise keep the current imeId.
+     *
+     * <p>1. If the device changes to virtual and its custom IME is not available, then disable
+     * IME.</p>
+     * <p>2. If the device changes to virtual with valid custom IME, then return the custom IME. If
+     * the old device was default, then store the current imeId so it can be restored.</p>
+     * <p>3. If the device changes to default, restore the default device IME.</p>
+     * <p>4. Otherwise keep the current imeId.</p>
      */
     @GuardedBy("ImfLock.class")
     private String computeCurrentDeviceMethodIdLocked(String currentMethodId) {
@@ -2333,7 +2357,8 @@
     @Nullable
     private InputBindResult tryReuseConnectionLocked(@NonNull UserDataRepository.UserData userData,
             @NonNull ClientState cs) {
-        if (userData.mBindingController.hasMainConnection()) {
+        final var bindingController = userData.mBindingController;
+        if (bindingController.hasMainConnection()) {
             if (getCurMethodLocked() != null) {
                 // Return to client, and we will get back with it when
                 // we have had a session made for it.
@@ -2341,9 +2366,11 @@
                 requestClientSessionForAccessibilityLocked(cs);
                 return new InputBindResult(
                         InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
-                        null, null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
+                        null, null, null,
+                        bindingController.getCurId(),
+                        bindingController.getSequenceNumber(), false);
             } else {
-                final long lastBindTime = userData.mBindingController.getLastBindTime();
+                final long lastBindTime = bindingController.getLastBindTime();
                 long bindingDuration = SystemClock.uptimeMillis() - lastBindTime;
                 if (bindingDuration < TIME_TO_RECONNECT) {
                     // In this case we have connected to the service, but
@@ -2355,7 +2382,9 @@
                     // to see if we can get back in touch with the service.
                     return new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
-                            null, null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
+                            null, null, null,
+                            bindingController.getCurId(),
+                            bindingController.getSequenceNumber(), false);
                 } else {
                     EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
                             getSelectedMethodIdLocked(), bindingDuration, 0);
@@ -2374,12 +2403,12 @@
     /**
      * Find the display where the IME should be shown.
      *
-     * @param displayId the ID of the display where the IME client target is.
-     * @param checker instance of {@link ImeDisplayValidator} which is used for
-     *                checking display config to adjust the final target display.
-     * @return The ID of the display where the IME should be shown or
-     *         {@link android.view.Display#INVALID_DISPLAY} if the display has an ImePolicy of
-     *         {@link WindowManager#DISPLAY_IME_POLICY_HIDE}.
+     * @param displayId the ID of the display where the IME client target is
+     * @param checker   instance of {@link ImeDisplayValidator} which is used for
+     *                  checking display config to adjust the final target display
+     * @return the ID of the display where the IME should be shown or
+     * {@link android.view.Display#INVALID_DISPLAY} if the display has an ImePolicy of
+     * {@link WindowManager#DISPLAY_IME_POLICY_HIDE}
      */
     static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) {
         if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
@@ -2402,7 +2431,7 @@
     void initializeImeLocked(@NonNull IInputMethodInvoker inputMethod, @NonNull IBinder token) {
         if (DEBUG) {
             Slog.v(TAG, "Sending attach of token: " + token + " for display: "
-                    + mCurTokenDisplayId);
+                    + getCurTokenDisplayIdLocked());
         }
         inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token),
                 getInputMethodNavButtonFlagsLocked());
@@ -2482,13 +2511,14 @@
 
     @GuardedBy("ImfLock.class")
     void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
-        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-        userData.mBindingController.setSelectedMethodId(null);
+        final var bindingController =
+                mUserDataRepository.getOrCreate(mCurrentUserId).mBindingController;
+        bindingController.setSelectedMethodId(null);
 
         // Callback before clean-up binding states.
         // TODO(b/338461930): Check if this is still necessary or not.
         onUnbindCurrentMethodByReset();
-        userData.mBindingController.unbindCurrentMethod();
+        bindingController.unbindCurrentMethod();
         unbindCurrentClientLocked(unbindClientReason);
     }
 
@@ -2691,9 +2721,10 @@
         }
         // Whether the current display has a navigation bar. When this is false (e.g. emulator),
         // the IME should not draw the IME navigation bar.
+        final int tokenDisplayId = getCurTokenDisplayIdLocked();
         final boolean hasNavigationBar = mWindowManagerInternal
-                .hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY
-                        ? mCurTokenDisplayId : DEFAULT_DISPLAY);
+                .hasNavigationBar(tokenDisplayId != INVALID_DISPLAY
+                        ? tokenDisplayId : DEFAULT_DISPLAY);
         final boolean canImeDrawsImeNavBar =
                 mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get() && hasNavigationBar;
         final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked(
@@ -2709,7 +2740,8 @@
         // When the IME switcher dialog is shown, the IME switcher button should be hidden.
         if (mMenuController.getSwitchingDialogLocked() != null) return false;
         // When we are switching IMEs, the IME switcher button should be hidden.
-        if (!Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) {
+        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+        if (!Objects.equals(userData.mBindingController.getCurId(), getSelectedMethodIdLocked())) {
             return false;
         }
         if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded()
@@ -2788,8 +2820,8 @@
             // Note that we still need to update IME status when focusing external display
             // that does not support system decoration and fallback to show IME on default
             // display since it is intentional behavior.
-            if (mCurTokenDisplayId != topFocusedDisplayId
-                    && mCurTokenDisplayId != FALLBACK_DISPLAY_ID) {
+            final int tokenDisplayId = getCurTokenDisplayIdLocked();
+            if (tokenDisplayId != topFocusedDisplayId && tokenDisplayId != FALLBACK_DISPLAY_ID) {
                 return;
             }
             mImeWindowVis = vis;
@@ -2852,7 +2884,7 @@
             Slog.d(TAG, "IME window vis: " + vis
                     + " active: " + (vis & InputMethodService.IME_ACTIVE)
                     + " inv: " + (vis & InputMethodService.IME_INVISIBLE)
-                    + " displayId: " + mCurTokenDisplayId);
+                    + " displayId: " + getCurTokenDisplayIdLocked());
         }
         final IBinder focusedWindowToken = mImeBindingState != null
                 ? mImeBindingState.mFocusedWindow : null;
@@ -2871,15 +2903,17 @@
             } else {
                 vis &= ~InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
             }
+            final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+            final var curId = userData.mBindingController.getCurId();
             if (mMenuController.getSwitchingDialogLocked() != null
-                    || !Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) {
+                    || !Objects.equals(curId, getSelectedMethodIdLocked())) {
                 // When the IME switcher dialog is shown, or we are switching IMEs,
                 // the back button should be in the default state (as if the IME is not shown).
                 backDisposition = InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING;
             }
             final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
             if (mStatusBarManagerInternal != null) {
-                mStatusBarManagerInternal.setImeWindowStatus(mCurTokenDisplayId,
+                mStatusBarManagerInternal.setImeWindowStatus(getCurTokenDisplayIdLocked(),
                         getCurTokenLocked(), vis, backDisposition, needsToShowImeSwitcher);
             }
         } finally {
@@ -3567,12 +3601,15 @@
                     "InputMethodManagerService#startInputOrWindowGainedFocus", mDumper);
             final InputBindResult result;
             synchronized (ImfLock.class) {
+                final var userData = mUserDataRepository.getOrCreate(userId);
+                final var bindingController = userData.mBindingController;
                 // If the system is not yet ready, we shouldn't be running third party code.
                 if (!mSystemReady) {
                     return new InputBindResult(
                             InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
                             null /* method */, null /* accessibilitySessions */, null /* channel */,
-                            getSelectedMethodIdLocked(), getSequenceNumberLocked(),
+                            getSelectedMethodIdLocked(),
+                            bindingController.getSequenceNumber(),
                             false /* isInputMethodSuppressingSpellChecker */);
                 }
                 final ClientState cs = mClientController.getClient(client.asBinder());
@@ -3664,7 +3701,7 @@
                     result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
                             client, windowToken, startInputFlags, softInputMode, windowFlags,
                             editorInfo, inputConnection, remoteAccessibilityInputConnection,
-                            unverifiedTargetSdkVersion, userId, imeDispatcher, cs);
+                            unverifiedTargetSdkVersion, userData, imeDispatcher, cs);
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -3692,7 +3729,7 @@
             @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo editorInfo,
             IRemoteInputConnection inputContext,
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            int unverifiedTargetSdkVersion, @NonNull UserDataRepository.UserData userData,
             @NonNull ImeOnBackInvokedDispatcher imeDispatcher, @NonNull ClientState cs) {
         if (DEBUG) {
             Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason="
@@ -3705,7 +3742,7 @@
                     + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
                     + " windowFlags=#" + Integer.toHexString(windowFlags)
                     + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion
-                    + " userId=" + userId
+                    + " userData=" + userData
                     + " imeDispatcher=" + imeDispatcher
                     + " cs=" + cs);
         }
@@ -3724,7 +3761,6 @@
                 startInputByWinGainedFocus, toolType);
         mVisibilityStateComputer.setWindowState(windowToken, windowState);
 
-        final var userData = mUserDataRepository.getOrCreate(userId);
         if (sameWindowFocused && isTextEditor) {
             if (DEBUG) {
                 Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
@@ -3742,7 +3778,8 @@
                     null, null, null, null, -1, false);
         }
 
-        mImeBindingState = new ImeBindingState(windowToken, softInputMode, cs, editorInfo);
+        mImeBindingState = new ImeBindingState(userData.mUserId, windowToken, softInputMode, cs,
+                editorInfo);
         mFocusedWindowPerceptible.put(windowToken, true);
 
         // We want to start input before showing the IME, but after closing
@@ -3781,7 +3818,7 @@
                 // window token removed.
                 // Note that we can trust client's display ID as long as it matches
                 // to the display ID obtained from the window.
-                if (cs.mSelfReportedDisplayId != mCurTokenDisplayId) {
+                if (cs.mSelfReportedDisplayId != getCurTokenDisplayIdLocked()) {
                     userData.mBindingController.unbindCurrentMethod();
                 }
             }
@@ -3832,10 +3869,10 @@
         if (mCurrentUserId != UserHandle.getUserId(uid)) {
             return false;
         }
-        if (getCurIntentLocked() != null && InputMethodUtils.checkIfPackageBelongsToUid(
-                mPackageManagerInternal,
-                uid,
-                getCurIntentLocked().getComponent().getPackageName())) {
+        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+        final var curIntent = userData.mBindingController.getCurIntent();
+        if (curIntent != null && InputMethodUtils.checkIfPackageBelongsToUid(
+                mPackageManagerInternal, uid, curIntent.getComponent().getPackageName())) {
             return true;
         }
         return false;
@@ -4145,7 +4182,6 @@
      * This is kept due to {@code @UnsupportedAppUsage} in
      * {@link InputMethodManager#getInputMethodWindowVisibleHeight()} and a dependency in
      * {@link InputMethodService#onCreate()}.
-     *
      * @return {@link WindowManagerInternal#getInputMethodWindowVisibleHeight(int)}
      *
      * @deprecated TODO(b/113914148): Check if we can remove this
@@ -4163,7 +4199,7 @@
                 }
                 // This should probably use the caller's display id, but because this is unsupported
                 // and maintained only for compatibility, there's no point in fixing it.
-                curTokenDisplayId = mCurTokenDisplayId;
+                curTokenDisplayId = getCurTokenDisplayIdLocked();
             }
             return mWindowManagerInternal.getInputMethodWindowVisibleHeight(curTokenDisplayId);
         });
@@ -4244,8 +4280,9 @@
         // a new Stylus is detected. If IME supports handwriting, and we don't have
         // handwriting initialized, lets do it now.
         final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+        final var bindingController = userData.mBindingController;
         if (!mHwController.getCurrentRequestId().isPresent()
-                && userData.mBindingController.supportsStylusHandwriting()) {
+                && bindingController.supportsStylusHandwriting()) {
             scheduleResetStylusHandwriting();
         }
     }
@@ -4303,7 +4340,8 @@
     /**
      * Helper method to set a stylus idle-timeout after which handwriting {@code InkWindow}
      * will be removed.
-     * @param timeout to set in milliseconds. To reset to default, use a value <= zero.
+     *
+     * @param timeout to set in milliseconds. To reset to default, use a value <= zero
      */
     @BinderThread
     @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
@@ -4427,9 +4465,10 @@
     private void dumpDebug(ProtoOutputStream proto, long fieldId) {
         synchronized (ImfLock.class) {
             final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+            final var bindingController = userData.mBindingController;
             final long token = proto.start(fieldId);
             proto.write(CUR_METHOD_ID, getSelectedMethodIdLocked());
-            proto.write(CUR_SEQ, getSequenceNumberLocked());
+            proto.write(CUR_SEQ, bindingController.getSequenceNumber());
             proto.write(CUR_CLIENT, Objects.toString(mCurClient));
             mImeBindingState.dumpDebug(proto, mWindowManagerInternal);
             proto.write(LAST_IME_TARGET_WINDOW_NAME,
@@ -4439,13 +4478,13 @@
             if (mCurEditorInfo != null) {
                 mCurEditorInfo.dumpDebug(proto, CUR_ATTRIBUTE);
             }
-            proto.write(CUR_ID, getCurIdLocked());
+            proto.write(CUR_ID, bindingController.getCurId());
             mVisibilityStateComputer.dumpDebug(proto, fieldId);
             proto.write(IN_FULLSCREEN_MODE, mInFullscreenMode);
             proto.write(CUR_TOKEN, Objects.toString(getCurTokenLocked()));
-            proto.write(CUR_TOKEN_DISPLAY_ID, mCurTokenDisplayId);
+            proto.write(CUR_TOKEN_DISPLAY_ID, getCurTokenDisplayIdLocked());
             proto.write(SYSTEM_READY, mSystemReady);
-            proto.write(HAVE_CONNECTION, userData.mBindingController.hasMainConnection());
+            proto.write(HAVE_CONNECTION, bindingController.hasMainConnection());
             proto.write(BOUND_TO_METHOD, mBoundToMethod);
             proto.write(IS_INTERACTIVE, mIsInteractive);
             proto.write(BACK_DISPOSITION, mBackDisposition);
@@ -4557,7 +4596,8 @@
         final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(requestImeToken);
         final WindowManagerInternal.ImeTargetInfo info =
                 mWindowManagerInternal.onToggleImeRequested(
-                        show, mImeBindingState.mFocusedWindow, requestToken, mCurTokenDisplayId);
+                        show, mImeBindingState.mFocusedWindow, requestToken,
+                        getCurTokenDisplayIdLocked());
         mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
                 mImeBindingState.mFocusedWindowClient, mImeBindingState.mFocusedWindowEditorInfo,
                 info.focusedWindowName, mImeBindingState.mFocusedWindowSoftInputMode, reason,
@@ -4816,10 +4856,11 @@
             case MSG_RESET_HANDWRITING: {
                 synchronized (ImfLock.class) {
                     final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-                    if (userData.mBindingController.supportsStylusHandwriting()
+                    final var bindingController = userData.mBindingController;
+                    if (bindingController.supportsStylusHandwriting()
                             && getCurMethodLocked() != null && hasSupportedStylusLocked()) {
                         Slog.d(TAG, "Initializing Handwriting Spy");
-                        mHwController.initializeHandwritingSpy(mCurTokenDisplayId);
+                        mHwController.initializeHandwritingSpy(getCurTokenDisplayIdLocked());
                     } else {
                         mHwController.reset();
                     }
@@ -4842,11 +4883,12 @@
                         return true;
                     }
                     final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+                    final var bindingController = userData.mBindingController;
                     final HandwritingModeController.HandwritingSession session =
                             mHwController.startHandwritingSession(
                                     msg.arg1 /*requestId*/,
                                     msg.arg2 /*pid*/,
-                                    userData.mBindingController.getCurMethodUid(),
+                                    bindingController.getCurMethodUid(),
                                     mImeBindingState.mFocusedWindow);
                     if (session == null) {
                         Slog.e(TAG,
@@ -4896,7 +4938,11 @@
             if (mCurClient == null || mCurClient.mClient == null) {
                 return;
             }
-            if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(getCurMethodUidLocked())) {
+            // TODO(b/325515685): user data must be retrieved by a userId parameter
+            final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+            final var bindingController = userData.mBindingController;
+            if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(
+                    bindingController.getCurMethodUid())) {
                 // Handle IME visibility when interactive changed before finishing the input to
                 // ensure we preserve the last state as possible.
                 final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged(
@@ -5141,7 +5187,8 @@
     @GuardedBy("ImfLock.class")
     void sendOnNavButtonFlagsChangedLocked() {
         final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-        final IInputMethodInvoker curMethod = userData.mBindingController.getCurMethod();
+        final var bindingController = userData.mBindingController;
+        final IInputMethodInvoker curMethod = bindingController.getCurMethod();
         if (curMethod == null) {
             // No need to send the data if the IME is not yet bound.
             return;
@@ -5186,10 +5233,10 @@
     /**
      * Enable or disable the given IME by updating {@link Settings.Secure#ENABLED_INPUT_METHODS}.
      *
-     * @param id ID of the IME is to be manipulated. It is OK to pass IME ID that is currently not
-     *           recognized by the system.
-     * @param enabled {@code true} if {@code id} needs to be enabled.
-     * @return {@code true} if the IME was previously enabled. {@code false} otherwise.
+     * @param id      ID of the IME is to be manipulated. It is OK to pass IME ID that is currently
+     *                not recognized by the system
+     * @param enabled {@code true} if {@code id} needs to be enabled
+     * @return {@code true} if the IME was previously enabled
      */
     @GuardedBy("ImfLock.class")
     private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
@@ -5296,8 +5343,8 @@
     /**
      * Gets the current subtype of this input method.
      *
-     * @param userId User ID to be queried about.
-     * @return The current {@link InputMethodSubtype} for the specified user.
+     * @param userId User ID to be queried about
+     * @return the current {@link InputMethodSubtype} for the specified user
      */
     @Nullable
     @Override
@@ -5371,7 +5418,8 @@
 
     /**
      * Returns the default {@link InputMethodInfo} for the specific userId.
-     * @param userId user ID to query.
+     *
+     * @param userId user ID to query
      */
     @GuardedBy("ImfLock.class")
     private InputMethodInfo queryDefaultInputMethodForUserIdLocked(@UserIdInt int userId) {
@@ -5405,11 +5453,11 @@
      * Filter the access to the input method by rules of the package visibility. Return {@code true}
      * if the given input method is the currently selected one or visible to the caller.
      *
-     * @param targetPkgName The package name of input method to check.
-     * @param callingUid The caller that is going to access the input method.
-     * @param userId The user ID where the input method resides.
-     * @param settings The input method settings under the given user ID.
-     * @return {@code true} if caller is able to access the input method.
+     * @param targetPkgName the package name of input method to check
+     * @param callingUid    the caller that is going to access the input method
+     * @param userId        the user ID where the input method resides
+     * @param settings      the input method settings under the given user ID
+     * @return {@code true} if caller is able to access the input method
      */
     private boolean canCallerAccessInputMethod(@NonNull String targetPkgName, int callingUid,
             @UserIdInt int userId, @NonNull InputMethodSettings settings) {
@@ -5503,7 +5551,7 @@
 
         @Override
         public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
-                InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) {
+                InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback cb) {
             // Get the device global touch exploration state before lock to avoid deadlock.
             final boolean touchExplorationEnabled = AccessibilityManagerInternal.get()
                     .isTouchExplorationEnabled(userId);
@@ -5575,7 +5623,7 @@
             //TODO(b/150843766): Check if Input Token is valid.
             final IBinder curHostInputToken;
             synchronized (ImfLock.class) {
-                if (displayId != mCurTokenDisplayId) {
+                if (displayId != getCurTokenDisplayIdLocked()) {
                     return false;
                 }
                 curHostInputToken = mAutofillController.getCurHostInputToken();
@@ -5626,6 +5674,8 @@
         public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
                 IAccessibilityInputMethodSession session, @UserIdInt int userId) {
             synchronized (ImfLock.class) {
+                final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+                final var bindingController = userData.mBindingController;
                 // TODO(b/305829876): Implement user ID verification
                 if (mCurClient != null) {
                     clearClientSessionForAccessibilityLocked(mCurClient, accessibilityConnectionId);
@@ -5647,8 +5697,10 @@
                                     mCurClient.mAccessibilitySessions);
                     final InputBindResult res = new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION,
-                            imeSession, accessibilityInputMethodSessions, null, getCurIdLocked(),
-                            getSequenceNumberLocked(), false);
+                            imeSession, accessibilityInputMethodSessions, /* channel= */ null,
+                            bindingController.getCurId(),
+                            bindingController.getSequenceNumber(),
+                            /* isInputMethodSuppressingSpellChecker= */ false);
                     mCurClient.mClient.onBindAccessibilityService(res, accessibilityConnectionId);
                 }
             }
@@ -5658,6 +5710,8 @@
         public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId,
                 @UserIdInt int userId) {
             synchronized (ImfLock.class) {
+                final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+                final var bindingController = userData.mBindingController;
                 // TODO(b/305829876): Implement user ID verification
                 if (mCurClient != null) {
                     if (DEBUG) {
@@ -5667,7 +5721,7 @@
                     // A11yManagerService unbinds the disabled accessibility service. We don't need
                     // to do it here.
                     mCurClient.mClient.onUnbindAccessibilityService(
-                            getSequenceNumberLocked(),
+                            bindingController.getSequenceNumber(),
                             accessibilityConnectionId);
                 }
                 // We only have sessions when we bound to an input method. Remove this session
@@ -5877,13 +5931,11 @@
             @SuppressWarnings("GuardedBy") Consumer<ClientState> clientControllerDump = c -> {
                 p.println("  " + c + ":");
                 p.println("    client=" + c.mClient);
-
                 p.println("    fallbackInputConnection="
                         + c.mFallbackInputConnection);
                 p.println("    sessionRequested="
                         + c.mSessionRequested);
-                p.println(
-                        "    sessionRequestedForAccessibility="
+                p.println("    sessionRequestedForAccessibility="
                                 + c.mSessionRequestedForAccessibility);
                 p.println("    curSession=" + c.mCurSession);
                 p.println("    selfReportedDisplayId=" + c.mSelfReportedDisplayId);
@@ -5891,17 +5943,20 @@
                 p.println("    pid=" + c.mPid);
             };
             mClientController.forAllClients(clientControllerDump);
+            final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+            final var bindingController = userData.mBindingController;
             p.println("  mCurrentUserId=" + mCurrentUserId);
             p.println("  mCurMethodId=" + getSelectedMethodIdLocked());
             client = mCurClient;
-            p.println("  mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked());
+            p.println("  mCurClient=" + client + " mCurSeq="
+                    + bindingController.getSequenceNumber());
             p.println("  mFocusedWindowPerceptible=" + mFocusedWindowPerceptible);
             mImeBindingState.dump(/* prefix= */ "  ", p);
-            final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-            p.println("  mCurId=" + getCurIdLocked()
-                    + " mHaveConnection=" + userData.mBindingController.hasMainConnection()
+
+            p.println("  mCurId=" + bindingController.getCurId()
+                    + " mHaveConnection=" + bindingController.hasMainConnection()
                     + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
-                    + userData.mBindingController.isVisibleBound());
+                    + bindingController.isVisibleBound());
 
             p.println("  mUserDataRepository=");
             // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed.
@@ -5915,15 +5970,17 @@
             mUserDataRepository.forAllUserData(userDataDump);
 
             p.println("  mCurToken=" + getCurTokenLocked());
-            p.println("  mCurTokenDisplayId=" + mCurTokenDisplayId);
+            p.println("  mCurTokenDisplayId=" + getCurTokenDisplayIdLocked());
             p.println("  mCurHostInputToken=" + mAutofillController.getCurHostInputToken());
-            p.println("  mCurIntent=" + getCurIntentLocked());
+            p.println("  mCurIntent=" + bindingController.getCurIntent());
             method = getCurMethodLocked();
             p.println("  mCurMethod=" + getCurMethodLocked());
             p.println("  mEnabledSession=" + mEnabledSession);
             mVisibilityStateComputer.dump(pw, "  ");
             p.println("  mInFullscreenMode=" + mInFullscreenMode);
             p.println("  mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
+            p.println("  mExperimentalConcurrentMultiUserModeEnabled="
+                    + mExperimentalConcurrentMultiUserModeEnabled);
             p.println("  ENABLE_HIDE_IME_CAPTION_BAR="
                     + InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR);
             p.println("  mSettingsObserver=" + mSettingsObserver);
@@ -6156,8 +6213,9 @@
 
     /**
      * Handles {@code adb shell ime list}.
-     * @param shellCommand {@link ShellCommand} object that is handling this command.
-     * @return Exit code of the command.
+     *
+     * @param shellCommand {@link ShellCommand} object that is handling this command
+     * @return exit code of the command
      */
     @BinderThread
     @ShellCommandResult
@@ -6215,9 +6273,9 @@
     /**
      * Handles {@code adb shell ime enable} and {@code adb shell ime disable}.
      *
-     * @param shellCommand {@link ShellCommand} object that is handling this command.
-     * @param enabled      {@code true} if the command was {@code adb shell ime enable}.
-     * @return Exit code of the command.
+     * @param shellCommand {@link ShellCommand} object that is handling this command
+     * @param enabled      {@code true} if the command was {@code adb shell ime enable}
+     * @return exit code of the command
      */
     @BinderThread
     @ShellCommandResult
@@ -6252,8 +6310,8 @@
      * {@link ShellCommand#getNextArg()} and {@link ShellCommand#getNextArgRequired()} for the
      * main arguments.</p>
      *
-     * @param shellCommand {@link ShellCommand} from which options should be obtained.
-     * @return User ID to be resolved. {@link UserHandle#CURRENT} if not specified.
+     * @param shellCommand {@link ShellCommand} from which options should be obtained
+     * @return user ID to be resolved. {@link UserHandle#CURRENT} if not specified
      */
     @BinderThread
     @UserIdInt
@@ -6275,12 +6333,12 @@
     /**
      * Handles core logic of {@code adb shell ime enable} and {@code adb shell ime disable}.
      *
-     * @param userId user ID specified to the command.  Pseudo user IDs are not supported.
-     * @param imeId IME ID specified to the command.
-     * @param enabled {@code true} for {@code adb shell ime enable}. {@code false} otherwise.
-     * @param out {@link PrintWriter} to output standard messages.
-     * @param error {@link PrintWriter} to output error messages.
-     * @return {@code false} if it fails to enable the IME.  {@code false} otherwise.
+     * @param userId  user ID specified to the command (pseudo user IDs are not supported)
+     * @param imeId   IME ID specified to the command
+     * @param enabled {@code true} for {@code adb shell ime enable}
+     * @param out     {@link PrintWriter} to output standard messages
+     * @param error   {@link PrintWriter} to output error messages
+     * @return {@code false} if it fails to enable the IME
      */
     @BinderThread
     @GuardedBy("ImfLock.class")
@@ -6338,7 +6396,7 @@
     /**
      * Handles {@code adb shell ime set}.
      *
-     * @param shellCommand {@link ShellCommand} object that is handling this command.
+     * @param shellCommand {@link ShellCommand} object that is handling this command
      * @return Exit code of the command.
      */
     @BinderThread
@@ -6381,7 +6439,8 @@
 
     /**
      * Handles {@code adb shell ime reset-ime}.
-     * @param shellCommand {@link ShellCommand} object that is handling this command.
+     *
+     * @param shellCommand {@link ShellCommand} object that is handling this command
      * @return Exit code of the command.
      */
     @BinderThread
@@ -6408,7 +6467,8 @@
                         hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
                                 SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
                         final var userData = mUserDataRepository.getOrCreate(userId);
-                        userData.mBindingController.unbindCurrentMethod();
+                        final var bindingController = userData.mBindingController;
+                        bindingController.unbindCurrentMethod();
 
                         // Enable default IMEs, disable others
                         var toDisable = settings.getEnabledInputMethodList();
@@ -6461,7 +6521,8 @@
 
     /**
      * Handles {@code adb shell cmd input_method tracing start/stop/save-for-bugreport}.
-     * @param shellCommand {@link ShellCommand} object that is handling this command.
+     *
+     * @param shellCommand {@link ShellCommand} object that is handling this command
      * @return Exit code of the command.
      */
     @BinderThread
@@ -6506,9 +6567,9 @@
 
     /**
      * @param userId the actual user handle obtained by {@link UserHandle#getIdentifier()}
-     * and *not* pseudo ids like {@link UserHandle#USER_ALL etc}.
-     * @return {@code true} if userId has debugging privileges.
-     * i.e. {@link UserManager#DISALLOW_DEBUGGING_FEATURES} is {@code false}.
+     *               and *not* pseudo ids like {@link UserHandle#USER_ALL etc}
+     * @return {@code true} if userId has debugging privileges
+     * i.e. {@link UserManager#DISALLOW_DEBUGGING_FEATURES} is {@code false}
      */
     private boolean userHasDebugPriv(@UserIdInt int userId, ShellCommand shellCommand) {
         if (mUserManagerInternal.hasUserRestriction(
@@ -6529,8 +6590,8 @@
     /**
      * Creates an IME request tracking token for the current focused client.
      *
-     * @param show whether this is a show or a hide request.
-     * @param reason the reason why the IME request was created.
+     * @param show   whether this is a show or a hide request
+     * @param reason the reason why the IME request was created
      */
     @NonNull
     private ImeTracker.Token createStatsTokenForFocusedClient(boolean show,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMap.java b/services/core/java/com/android/server/inputmethod/InputMethodMap.java
index a8e5e2e..bab21e8 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMap.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMap.java
@@ -23,6 +23,7 @@
 import android.util.ArrayMap;
 import android.view.inputmethod.InputMethodInfo;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -75,4 +76,61 @@
     int size() {
         return mMap.size();
     }
+
+    @AnyThread
+    @NonNull
+    public InputMethodMap applyAdditionalSubtypes(
+            @NonNull AdditionalSubtypeMap additionalSubtypeMap) {
+        if (additionalSubtypeMap.isEmpty()) {
+            return this;
+        }
+        final int size = size();
+        final ArrayMap<String, InputMethodInfo> newMethodMap = new ArrayMap<>(size);
+        boolean updated = false;
+        for (int i = 0; i < size; ++i) {
+            final var imi = valueAt(i);
+            final var imeId = imi.getId();
+            final var newAdditionalSubtypes = additionalSubtypeMap.get(imeId);
+            if (newAdditionalSubtypes == null || newAdditionalSubtypes.isEmpty()) {
+                newMethodMap.put(imi.getId(), imi);
+            } else {
+                newMethodMap.put(imi.getId(), new InputMethodInfo(imi, newAdditionalSubtypes));
+                updated = true;
+            }
+        }
+        return updated ? InputMethodMap.of(newMethodMap) : this;
+    }
+
+    /**
+     * Compares the given two {@link InputMethodMap} instances to see if they contain the same data
+     * or not.
+     *
+     * @param map1 {@link InputMethodMap} to be compared with
+     * @param map2 {@link InputMethodMap} to be compared with
+     * @return {@code true} if both {@link InputMethodMap} instances contain exactly the same data
+     */
+    @AnyThread
+    static boolean areSame(@NonNull InputMethodMap map1, @NonNull InputMethodMap map2) {
+        if (map1 == map2) {
+            return true;
+        }
+        final int size = map1.size();
+        if (size != map2.size()) {
+            return false;
+        }
+        for (int i = 0; i < size; ++i) {
+            final var imi1 = map1.valueAt(i);
+            final var imeId = imi1.getId();
+            final var imi2 = map2.get(imeId);
+            if (imi2 == null) {
+                return false;
+            }
+            final var marshaled1 = InputMethodInfoUtils.marshal(imi1);
+            final var marshaled2 = InputMethodInfoUtils.marshal(imi2);
+            if (!Arrays.equals(marshaled1, marshaled2)) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/UserDataRepository.java b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
index 825cfcb..2b19d3e 100644
--- a/services/core/java/com/android/server/inputmethod/UserDataRepository.java
+++ b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
@@ -96,5 +96,10 @@
             mUserId = userId;
             mBindingController = bindingController;
         }
+
+        @Override
+        public String toString() {
+            return "UserData{" + "mUserId=" + mUserId + '}';
+        }
     }
 }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index a0dbfa0..ec94e2b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -412,10 +412,15 @@
     /* package */
     synchronized void addTransaction(
             ContextHubServiceTransaction transaction) throws IllegalStateException {
+        if (transaction == null) {
+            return;
+        }
+
         if (mTransactionQueue.size() == MAX_PENDING_REQUESTS) {
             throw new IllegalStateException("Transaction queue is full (capacity = "
                     + MAX_PENDING_REQUESTS + ")");
         }
+
         mTransactionQueue.add(transaction);
         mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
 
@@ -517,7 +522,10 @@
      * the caller has obtained a lock on this ContextHubTransactionManager object.
      */
     private void removeTransactionAndStartNext() {
-        mTimeoutFuture.cancel(false /* mayInterruptIfRunning */);
+        if (mTimeoutFuture != null) {
+            mTimeoutFuture.cancel(/* mayInterruptIfRunning= */ false);
+            mTimeoutFuture = null;
+        }
 
         ContextHubServiceTransaction transaction = mTransactionQueue.remove();
         transaction.setComplete();
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 73647db..e1f8939 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -2800,6 +2800,10 @@
             final String providerId = route.getProviderId();
             final MediaRoute2Provider provider = findProvider(providerId);
             if (provider == null) {
+                Slog.w(
+                        TAG,
+                        "Ignoring transferToRoute due to lack of matching provider for target: "
+                                + route);
                 return;
             }
             provider.transferToRoute(
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index a3c5d2d..69f07d5 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -1057,6 +1057,7 @@
         return -1;
     }
 
+    @NonNull
     private PlaybackInfo getVolumeAttributes() {
         int volumeType;
         AudioAttributes attributes;
@@ -1850,6 +1851,7 @@
             return mFlags;
         }
 
+        @NonNull
         @Override
         public PlaybackInfo getVolumeAttributes() {
             return MediaSessionRecord.this.getVolumeAttributes();
diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java
index d25f529..5ea3e70 100644
--- a/services/core/java/com/android/server/net/NetworkManagementService.java
+++ b/services/core/java/com/android/server/net/NetworkManagementService.java
@@ -20,6 +20,9 @@
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
@@ -31,6 +34,9 @@
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_ALLOW;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_USER;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
@@ -143,6 +149,8 @@
     private final Object mQuotaLock = new Object();
     private final Object mRulesLock = new Object();
 
+    private final boolean mUseMeteredFirewallChains;
+
     /** Set of interfaces with active quotas. */
     @GuardedBy("mQuotaLock")
     private HashMap<String, Long> mActiveQuotas = Maps.newHashMap();
@@ -150,9 +158,11 @@
     @GuardedBy("mQuotaLock")
     private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
     /** Set of UIDs denied on metered networks. */
+    // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains.
     @GuardedBy("mRulesLock")
     private SparseBooleanArray mUidRejectOnMetered = new SparseBooleanArray();
     /** Set of UIDs allowed on metered networks. */
+    // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains.
     @GuardedBy("mRulesLock")
     private SparseBooleanArray mUidAllowOnMetered = new SparseBooleanArray();
     /** Set of UIDs with cleartext penalties. */
@@ -196,10 +206,32 @@
     @GuardedBy("mRulesLock")
     private final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray();
 
+    /**
+     * Contains the per-UID firewall rules that are used to allowlist the app from metered-network
+     * restrictions when data saver is enabled.
+     */
+    @GuardedBy("mRulesLock")
+    private final SparseIntArray mUidMeteredFirewallAllowRules = new SparseIntArray();
+
+    /**
+     * Contains the per-UID firewall rules that are used to deny app access to metered networks
+     * due to user action.
+     */
+    @GuardedBy("mRulesLock")
+    private final SparseIntArray mUidMeteredFirewallDenyUserRules = new SparseIntArray();
+
+    /**
+     * Contains the per-UID firewall rules that are used to deny app access to metered networks
+     * due to admin action.
+     */
+    @GuardedBy("mRulesLock")
+    private final SparseIntArray mUidMeteredFirewallDenyAdminRules = new SparseIntArray();
+
     /** Set of states for the child firewall chains. True if the chain is active. */
     @GuardedBy("mRulesLock")
     final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
 
+    // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains.
     @GuardedBy("mQuotaLock")
     private volatile boolean mDataSaverMode;
 
@@ -217,6 +249,15 @@
         mContext = context;
         mDeps = deps;
 
+        mUseMeteredFirewallChains = Flags.useMeteredFirewallChains();
+
+        if (mUseMeteredFirewallChains) {
+            // These firewalls are always on and currently ConnectivityService does not allow
+            // changing their enabled state.
+            mFirewallChainStates.put(FIREWALL_CHAIN_METERED_DENY_USER, true);
+            mFirewallChainStates.put(FIREWALL_CHAIN_METERED_DENY_ADMIN, true);
+        }
+
         mDaemonHandler = new Handler(FgThread.get().getLooper());
 
         mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener();
@@ -410,33 +451,39 @@
                 }
             }
 
-            SparseBooleanArray uidRejectOnQuota = null;
-            SparseBooleanArray uidAcceptOnQuota = null;
-            synchronized (mRulesLock) {
-                size = mUidRejectOnMetered.size();
-                if (size > 0) {
-                    if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered denylist rules");
-                    uidRejectOnQuota = mUidRejectOnMetered;
-                    mUidRejectOnMetered = new SparseBooleanArray();
-                }
+            if (!mUseMeteredFirewallChains) {
+                SparseBooleanArray uidRejectOnQuota = null;
+                SparseBooleanArray uidAcceptOnQuota = null;
+                synchronized (mRulesLock) {
+                    size = mUidRejectOnMetered.size();
+                    if (size > 0) {
+                        if (DBG) {
+                            Slog.d(TAG, "Pushing " + size + " UIDs to metered denylist rules");
+                        }
+                        uidRejectOnQuota = mUidRejectOnMetered;
+                        mUidRejectOnMetered = new SparseBooleanArray();
+                    }
 
-                size = mUidAllowOnMetered.size();
-                if (size > 0) {
-                    if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered allowlist rules");
-                    uidAcceptOnQuota = mUidAllowOnMetered;
-                    mUidAllowOnMetered = new SparseBooleanArray();
+                    size = mUidAllowOnMetered.size();
+                    if (size > 0) {
+                        if (DBG) {
+                            Slog.d(TAG, "Pushing " + size + " UIDs to metered allowlist rules");
+                        }
+                        uidAcceptOnQuota = mUidAllowOnMetered;
+                        mUidAllowOnMetered = new SparseBooleanArray();
+                    }
                 }
-            }
-            if (uidRejectOnQuota != null) {
-                for (int i = 0; i < uidRejectOnQuota.size(); i++) {
-                    setUidOnMeteredNetworkDenylist(uidRejectOnQuota.keyAt(i),
-                            uidRejectOnQuota.valueAt(i));
+                if (uidRejectOnQuota != null) {
+                    for (int i = 0; i < uidRejectOnQuota.size(); i++) {
+                        setUidOnMeteredNetworkDenylist(uidRejectOnQuota.keyAt(i),
+                                uidRejectOnQuota.valueAt(i));
+                    }
                 }
-            }
-            if (uidAcceptOnQuota != null) {
-                for (int i = 0; i < uidAcceptOnQuota.size(); i++) {
-                    setUidOnMeteredNetworkAllowlist(uidAcceptOnQuota.keyAt(i),
-                            uidAcceptOnQuota.valueAt(i));
+                if (uidAcceptOnQuota != null) {
+                    for (int i = 0; i < uidAcceptOnQuota.size(); i++) {
+                        setUidOnMeteredNetworkAllowlist(uidAcceptOnQuota.keyAt(i),
+                                uidAcceptOnQuota.valueAt(i));
+                    }
                 }
             }
 
@@ -459,8 +506,16 @@
             syncFirewallChainLocked(FIREWALL_CHAIN_RESTRICTED, "restricted ");
             syncFirewallChainLocked(FIREWALL_CHAIN_LOW_POWER_STANDBY, "low power standby ");
             syncFirewallChainLocked(FIREWALL_CHAIN_BACKGROUND, FIREWALL_CHAIN_NAME_BACKGROUND);
+            if (mUseMeteredFirewallChains) {
+                syncFirewallChainLocked(FIREWALL_CHAIN_METERED_ALLOW,
+                        FIREWALL_CHAIN_NAME_METERED_ALLOW);
+                syncFirewallChainLocked(FIREWALL_CHAIN_METERED_DENY_USER,
+                        FIREWALL_CHAIN_NAME_METERED_DENY_USER);
+                syncFirewallChainLocked(FIREWALL_CHAIN_METERED_DENY_ADMIN,
+                        FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN);
+            }
 
-            final int[] chains = {
+            final int[] chainsToEnable = {
                     FIREWALL_CHAIN_STANDBY,
                     FIREWALL_CHAIN_DOZABLE,
                     FIREWALL_CHAIN_POWERSAVE,
@@ -469,14 +524,13 @@
                     FIREWALL_CHAIN_BACKGROUND,
             };
 
-            for (int chain : chains) {
+            for (int chain : chainsToEnable) {
                 if (getFirewallChainState(chain)) {
                     setFirewallChainEnabled(chain, true);
                 }
             }
         }
 
-
         try {
             getBatteryStats().noteNetworkStatsEnabled();
         } catch (RemoteException e) {
@@ -1077,6 +1131,14 @@
                     mContext.getSystemService(ConnectivityManager.class)
                             .setDataSaverEnabled(enable);
                     mDataSaverMode = enable;
+                    if (mUseMeteredFirewallChains) {
+                        // Copy mDataSaverMode state to FIREWALL_CHAIN_METERED_ALLOW
+                        // until ConnectivityService allows manipulation of the data saver mode via
+                        // FIREWALL_CHAIN_METERED_ALLOW.
+                        synchronized (mRulesLock) {
+                            mFirewallChainStates.put(FIREWALL_CHAIN_METERED_ALLOW, enable);
+                        }
+                    }
                     return true;
                 } else {
                     final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
@@ -1191,9 +1253,9 @@
                 setFirewallChainState(chain, enable);
             }
 
-            final String chainName = getFirewallChainName(chain);
-            if (chain == FIREWALL_CHAIN_NONE) {
-                throw new IllegalArgumentException("Bad child chain: " + chainName);
+            if (!isValidFirewallChainForSetEnabled(chain)) {
+                throw new IllegalArgumentException("Invalid chain for setFirewallChainEnabled: "
+                        + NetworkPolicyLogger.getFirewallChainName(chain));
             }
 
             final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
@@ -1205,38 +1267,29 @@
         }
     }
 
-    private String getFirewallChainName(int chain) {
-        switch (chain) {
-            case FIREWALL_CHAIN_STANDBY:
-                return FIREWALL_CHAIN_NAME_STANDBY;
-            case FIREWALL_CHAIN_DOZABLE:
-                return FIREWALL_CHAIN_NAME_DOZABLE;
-            case FIREWALL_CHAIN_POWERSAVE:
-                return FIREWALL_CHAIN_NAME_POWERSAVE;
-            case FIREWALL_CHAIN_RESTRICTED:
-                return FIREWALL_CHAIN_NAME_RESTRICTED;
-            case FIREWALL_CHAIN_LOW_POWER_STANDBY:
-                return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
-            case FIREWALL_CHAIN_BACKGROUND:
-                return FIREWALL_CHAIN_NAME_BACKGROUND;
-            default:
-                throw new IllegalArgumentException("Bad child chain: " + chain);
-        }
+    private boolean isValidFirewallChainForSetEnabled(int chain) {
+        return switch (chain) {
+            case FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_POWERSAVE,
+                    FIREWALL_CHAIN_RESTRICTED, FIREWALL_CHAIN_LOW_POWER_STANDBY,
+                    FIREWALL_CHAIN_BACKGROUND -> true;
+            // METERED_* firewall chains are not yet supported by
+            // ConnectivityService#setFirewallChainEnabled.
+            default -> false;
+        };
     }
 
     private int getFirewallType(int chain) {
         switch (chain) {
             case FIREWALL_CHAIN_STANDBY:
+            case FIREWALL_CHAIN_METERED_DENY_ADMIN:
+            case FIREWALL_CHAIN_METERED_DENY_USER:
                 return FIREWALL_DENYLIST;
             case FIREWALL_CHAIN_DOZABLE:
-                return FIREWALL_ALLOWLIST;
             case FIREWALL_CHAIN_POWERSAVE:
-                return FIREWALL_ALLOWLIST;
             case FIREWALL_CHAIN_RESTRICTED:
-                return FIREWALL_ALLOWLIST;
             case FIREWALL_CHAIN_LOW_POWER_STANDBY:
-                return FIREWALL_ALLOWLIST;
             case FIREWALL_CHAIN_BACKGROUND:
+            case FIREWALL_CHAIN_METERED_ALLOW:
                 return FIREWALL_ALLOWLIST;
             default:
                 return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST;
@@ -1360,6 +1413,12 @@
                 return mUidFirewallLowPowerStandbyRules;
             case FIREWALL_CHAIN_BACKGROUND:
                 return mUidFirewallBackgroundRules;
+            case FIREWALL_CHAIN_METERED_ALLOW:
+                return mUidMeteredFirewallAllowRules;
+            case FIREWALL_CHAIN_METERED_DENY_USER:
+                return mUidMeteredFirewallDenyUserRules;
+            case FIREWALL_CHAIN_METERED_DENY_ADMIN:
+                return mUidMeteredFirewallDenyAdminRules;
             case FIREWALL_CHAIN_NONE:
                 return mUidFirewallRules;
             default:
@@ -1378,6 +1437,10 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
+        pw.println("Flags:");
+        pw.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": " + mUseMeteredFirewallChains);
+        pw.println();
+
         synchronized (mQuotaLock) {
             pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
             pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
@@ -1416,6 +1479,27 @@
             pw.print("UID firewall background chain enabled: ");
             pw.println(getFirewallChainState(FIREWALL_CHAIN_BACKGROUND));
             dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_BACKGROUND, mUidFirewallBackgroundRules);
+
+            pw.print("UID firewall metered allow chain enabled (Data saver mode): ");
+            // getFirewallChainState should maintain a duplicated state from mDataSaverMode when
+            // mUseMeteredFirewallChains is enabled.
+            pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_ALLOW));
+            dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_ALLOW,
+                    mUidMeteredFirewallAllowRules);
+
+            pw.print("UID firewall metered deny_user chain enabled (always-on): ");
+            // This always-on state should be reflected by getFirewallChainState when
+            // mUseMeteredFirewallChains is enabled.
+            pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_USER));
+            dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_DENY_USER,
+                    mUidMeteredFirewallDenyUserRules);
+
+            pw.print("UID firewall metered deny_admin chain enabled (always-on): ");
+            // This always-on state should be reflected by getFirewallChainState when
+            // mUseMeteredFirewallChains is enabled.
+            pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_ADMIN));
+            dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN,
+                    mUidMeteredFirewallDenyAdminRules);
         }
 
         pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
@@ -1520,14 +1604,40 @@
                 if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because it is in background");
                 return true;
             }
-            if (mUidRejectOnMetered.get(uid)) {
-                if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of no metered data"
-                        + " in the background");
-                return true;
-            }
-            if (mDataSaverMode && !mUidAllowOnMetered.get(uid)) {
-                if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode");
-                return true;
+            if (mUseMeteredFirewallChains) {
+                if (getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_USER)
+                        && mUidMeteredFirewallDenyUserRules.get(uid) == FIREWALL_RULE_DENY) {
+                    if (DBG) {
+                        Slog.d(TAG, "Uid " + uid + " restricted because of user-restricted metered"
+                                + " data in the background");
+                    }
+                    return true;
+                }
+                if (getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_ADMIN)
+                        && mUidMeteredFirewallDenyAdminRules.get(uid) == FIREWALL_RULE_DENY) {
+                    if (DBG) {
+                        Slog.d(TAG, "Uid " + uid + " restricted because of admin-restricted metered"
+                                + " data in the background");
+                    }
+                    return true;
+                }
+                if (getFirewallChainState(FIREWALL_CHAIN_METERED_ALLOW)
+                        && mUidMeteredFirewallAllowRules.get(uid) != FIREWALL_RULE_ALLOW) {
+                    if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode");
+                    return true;
+                }
+            } else {
+                if (mUidRejectOnMetered.get(uid)) {
+                    if (DBG) {
+                        Slog.d(TAG, "Uid " + uid
+                                + " restricted because of no metered data in the background");
+                    }
+                    return true;
+                }
+                if (mDataSaverMode && !mUidAllowOnMetered.get(uid)) {
+                    if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode");
+                    return true;
+                }
             }
             return false;
         }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 8e2d778..681aa8a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -19,6 +19,9 @@
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
@@ -28,6 +31,9 @@
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_ALLOW;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_USER;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
@@ -379,7 +385,7 @@
         return "Interfaces of netId=" + netId + " changed to " + newIfaces;
     }
 
-    private static String getFirewallChainName(int chain) {
+    static String getFirewallChainName(int chain) {
         switch (chain) {
             case FIREWALL_CHAIN_DOZABLE:
                 return FIREWALL_CHAIN_NAME_DOZABLE;
@@ -393,6 +399,12 @@
                 return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
             case FIREWALL_CHAIN_BACKGROUND:
                 return FIREWALL_CHAIN_NAME_BACKGROUND;
+            case FIREWALL_CHAIN_METERED_ALLOW:
+                return FIREWALL_CHAIN_NAME_METERED_ALLOW;
+            case FIREWALL_CHAIN_METERED_DENY_USER:
+                return FIREWALL_CHAIN_NAME_METERED_DENY_USER;
+            case FIREWALL_CHAIN_METERED_DENY_ADMIN:
+                return FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN;
             default:
                 return String.valueOf(chain);
         }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 22f5332..c60ac3a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -60,6 +60,9 @@
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
@@ -514,6 +517,12 @@
      */
     private boolean mBackgroundNetworkRestricted;
 
+    /**
+     * Whether or not metered firewall chains should be used for uid policy controlling access to
+     * metered networks.
+     */
+    private boolean mUseMeteredFirewallChains;
+
     // See main javadoc for instructions on how to use these locks.
     final Object mUidRulesFirstLock = new Object();
     final Object mNetworkPoliciesSecondLock = new Object();
@@ -997,6 +1006,8 @@
             mAppStandby = LocalServices.getService(AppStandbyInternal.class);
             mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
 
+            mUseMeteredFirewallChains = Flags.useMeteredFirewallChains();
+
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
                     updatePowerSaveAllowlistUL();
@@ -4030,8 +4041,10 @@
 
                 fout.println();
                 fout.println("Flags:");
-                fout.println("Network blocked for TOP_SLEEPING and above: "
+                fout.println(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE + ": "
                         + mBackgroundNetworkRestricted);
+                fout.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": "
+                        + mUseMeteredFirewallChains);
 
                 fout.println();
                 fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
@@ -5373,23 +5386,44 @@
             postUidRulesChangedMsg(uid, uidRules);
         }
 
-        // Note that the conditionals below are for avoiding unnecessary calls to netd.
-        // TODO: Measure the performance for doing a no-op call to netd so that we can
-        // remove the conditionals to simplify the logic below. We can also further reduce
-        // some calls to netd if they turn out to be costly.
-        final int denylistReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED
-                | BLOCKED_METERED_REASON_USER_RESTRICTED;
-        if ((oldEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE
-                || (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE) {
-            setMeteredNetworkDenylist(uid,
-                    (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE);
-        }
-        final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND
-                | ALLOWED_METERED_REASON_USER_EXEMPTED;
-        if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE
-                || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) {
-            setMeteredNetworkAllowlist(uid,
-                    (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE);
+        if (mUseMeteredFirewallChains) {
+            if ((newEffectiveBlockedReasons & BLOCKED_METERED_REASON_ADMIN_DISABLED)
+                    != BLOCKED_REASON_NONE) {
+                setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid, FIREWALL_RULE_DENY);
+            } else {
+                setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid, FIREWALL_RULE_DEFAULT);
+            }
+            if ((newEffectiveBlockedReasons & BLOCKED_METERED_REASON_USER_RESTRICTED)
+                    != BLOCKED_REASON_NONE) {
+                setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_DENY);
+            } else {
+                setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_DEFAULT);
+            }
+            if ((newAllowedReasons & (ALLOWED_METERED_REASON_FOREGROUND
+                    | ALLOWED_METERED_REASON_USER_EXEMPTED)) != ALLOWED_REASON_NONE) {
+                setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_ALLOW);
+            } else {
+                setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_DEFAULT);
+            }
+        } else {
+            // Note that the conditionals below are for avoiding unnecessary calls to netd.
+            // TODO: Measure the performance for doing a no-op call to netd so that we can
+            // remove the conditionals to simplify the logic below. We can also further reduce
+            // some calls to netd if they turn out to be costly.
+            final int denylistReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED
+                    | BLOCKED_METERED_REASON_USER_RESTRICTED;
+            if ((oldEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE
+                    || (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE) {
+                setMeteredNetworkDenylist(uid,
+                        (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE);
+            }
+            final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND
+                    | ALLOWED_METERED_REASON_USER_EXEMPTED;
+            if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE
+                    || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) {
+                setMeteredNetworkAllowlist(uid,
+                        (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE);
+            }
         }
     }
 
@@ -6149,6 +6183,8 @@
             } else if (chain == FIREWALL_CHAIN_BACKGROUND) {
                 mUidFirewallBackgroundRules.put(uid, rule);
             }
+            // Note that we do not need keep a separate cache of uid rules for chains that we do
+            // not call #setUidFirewallRulesUL for.
 
             try {
                 mNetworkManager.setFirewallUidRule(chain, uid, rule);
@@ -6206,10 +6242,19 @@
                     FIREWALL_RULE_DEFAULT);
             mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, uid,
                     FIREWALL_RULE_DEFAULT);
-            mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false);
-            mLogger.meteredAllowlistChanged(uid, false);
-            mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false);
-            mLogger.meteredDenylistChanged(uid, false);
+            if (mUseMeteredFirewallChains) {
+                mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid,
+                        FIREWALL_RULE_DEFAULT);
+                mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, uid,
+                        FIREWALL_RULE_DEFAULT);
+                mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, uid,
+                        FIREWALL_RULE_DEFAULT);
+            } else {
+                mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false);
+                mLogger.meteredAllowlistChanged(uid, false);
+                mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false);
+                mLogger.meteredDenylistChanged(uid, false);
+            }
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem resetting firewall uid rules for " + uid, e);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig
index d9491de..e986dd8 100644
--- a/services/core/java/com/android/server/net/flags.aconfig
+++ b/services/core/java/com/android/server/net/flags.aconfig
@@ -7,3 +7,13 @@
     description: "Block network access for apps in a low importance background state"
     bug: "304347838"
 }
+
+flag {
+    name: "use_metered_firewall_chains"
+    namespace: "backstage_power"
+    description: "Use metered firewall chains to control access to metered networks"
+    bug: "336693007"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index bf49671..13429db 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -22,12 +22,14 @@
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 import static android.media.audio.Flags.focusExclusiveWithRecording;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
 
+import android.Manifest.permission;
 import android.annotation.IntDef;
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
@@ -135,7 +137,7 @@
     private LogicalLight mAttentionLight;
 
     private final boolean mUseAttentionLight;
-    boolean mHasLight = true;
+    boolean mHasLight;
 
     private final SettingsObserver mSettingsObserver;
 
@@ -149,7 +151,7 @@
     private boolean mInCallStateOffHook = false;
     private boolean mScreenOn = true;
     private boolean mUserPresent = false;
-    boolean mNotificationPulseEnabled;
+    private boolean mNotificationPulseEnabled;
     private final Uri mInCallNotificationUri;
     private final AudioAttributes mInCallNotificationAudioAttributes;
     private final float mInCallNotificationVolume;
@@ -223,7 +225,10 @@
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
-                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET));
+                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET),
+                    record -> mPackageManager.checkPermission(
+                            permission.RECEIVE_EMERGENCY_BROADCAST,
+                            record.getSbn().getPackageName()) == PERMISSION_GRANTED);
 
             return new StrategyAvalanche(
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
@@ -231,14 +236,17 @@
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_AVALANCHE_TIMEOUT),
-                    appStrategy);
+                    appStrategy, appStrategy.mExemptionProvider);
         } else {
             return new StrategyPerApp(
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
-                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET));
+                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET),
+                    record -> mPackageManager.checkPermission(
+                            permission.RECEIVE_EMERGENCY_BROADCAST,
+                            record.getSbn().getPackageName()) == PERMISSION_GRANTED);
         }
     }
 
@@ -305,6 +313,13 @@
     }
 
     private void loadUserSettings() {
+        boolean pulseEnabled = Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
+        if (mNotificationPulseEnabled != pulseEnabled) {
+            mNotificationPulseEnabled = pulseEnabled;
+            updateLightsLocked();
+        }
+
         if (Flags.politeNotifications()) {
             try {
                 mCurrentWorkProfileId = getManagedProfileId(ActivityManager.getCurrentUser());
@@ -874,6 +889,9 @@
 
     boolean canShowLightsLocked(final NotificationRecord record, final Signals signals,
             boolean aboveThreshold) {
+        if (!mSystemReady) {
+            return false;
+        }
         // device lacks light
         if (!mHasLight) {
             return false;
@@ -1088,6 +1106,11 @@
         }
     }
 
+    // Returns true if a notification should be exempted from attenuation
+    private interface ExemptionProvider {
+        boolean isExempted(NotificationRecord record);
+    }
+
     @VisibleForTesting
     abstract static class PolitenessStrategy {
         static final int POLITE_STATE_DEFAULT = 0;
@@ -1118,8 +1141,10 @@
 
         protected boolean mIsActive = true;
 
+        protected final ExemptionProvider mExemptionProvider;
+
         public PolitenessStrategy(int timeoutPolite, int timeoutMuted, int volumePolite,
-                int volumeMuted) {
+                int volumeMuted, ExemptionProvider exemptionProvider) {
             mVolumeStates = new HashMap<>();
             mLastUpdatedTimestampByPackage = new HashMap<>();
 
@@ -1127,6 +1152,7 @@
             this.mTimeoutMuted = timeoutMuted;
             this.mVolumePolite = volumePolite / 100.0f;
             this.mVolumeMuted = volumeMuted / 100.0f;
+            this.mExemptionProvider = exemptionProvider;
         }
 
         abstract void onNotificationPosted(NotificationRecord record);
@@ -1284,8 +1310,8 @@
         private final int mMaxPostedForReset;
 
         public StrategyPerApp(int timeoutPolite, int timeoutMuted, int volumePolite,
-                int volumeMuted, int maxPosted) {
-            super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
+                int volumeMuted, int maxPosted, ExemptionProvider exemptionProvider) {
+            super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted, exemptionProvider);
 
             mNumPosted = new HashMap<>();
             mMaxPostedForReset = maxPosted;
@@ -1306,7 +1332,12 @@
 
             final String key = getChannelKey(record);
             @PolitenessState final int currState = getPolitenessState(record);
-            @PolitenessState int nextState = getNextState(currState, timeSinceLastNotif);
+            @PolitenessState int nextState;
+            if (Flags.politeNotificationsAttnUpdate()) {
+                nextState = getNextState(currState, timeSinceLastNotif, record);
+            } else {
+                nextState = getNextState(currState, timeSinceLastNotif);
+            }
 
             // Reset to default state if number of posted notifications exceed this value when muted
             int numPosted = mNumPosted.getOrDefault(key, 0) + 1;
@@ -1324,6 +1355,14 @@
             mVolumeStates.put(key, nextState);
         }
 
+        @PolitenessState int getNextState(@PolitenessState final int currState,
+                final long timeSinceLastNotif, final NotificationRecord record) {
+            if (mExemptionProvider.isExempted(record)) {
+                return POLITE_STATE_DEFAULT;
+            }
+            return getNextState(currState, timeSinceLastNotif);
+        }
+
         @Override
         public void onUserInteraction(final NotificationRecord record) {
             super.onUserInteraction(record);
@@ -1344,8 +1383,9 @@
         private long mLastAvalancheTriggerTimestamp = 0;
 
         StrategyAvalanche(int timeoutPolite, int timeoutMuted, int volumePolite,
-                    int volumeMuted, int timeoutAvalanche, PolitenessStrategy appStrategy) {
-            super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
+                    int volumeMuted, int timeoutAvalanche, PolitenessStrategy appStrategy,
+                    ExemptionProvider exemptionProvider) {
+            super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted, exemptionProvider);
 
             mTimeoutAvalanche = timeoutAvalanche;
             mAppStrategy = appStrategy;
@@ -1518,7 +1558,7 @@
                 return true;
             }
 
-            return false;
+            return mExemptionProvider.isExempted(record);
         }
 
         private boolean isAvalancheExempted(final NotificationRecord record) {
@@ -1721,8 +1761,6 @@
     void setLights(LogicalLight light) {
         mNotificationLight = light;
         mAttentionLight = light;
-        mNotificationPulseEnabled = true;
-        mHasLight = true;
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 61054a9..44e7694 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -593,6 +593,8 @@
 
     static final long NOTIFICATION_TTL = Duration.ofDays(3).toMillis();
 
+    static final long NOTIFICATION_MAX_AGE_AT_POST = Duration.ofDays(14).toMillis();
+
     private IActivityManager mAm;
     private ActivityTaskManagerInternal mAtm;
     private ActivityManager mActivityManager;
@@ -2637,27 +2639,48 @@
      * Cleanup broadcast receivers change listeners.
      */
     public void onDestroy() {
-        getContext().unregisterReceiver(mIntentReceiver);
-        getContext().unregisterReceiver(mPackageIntentReceiver);
-        if (Flags.allNotifsNeedTtl()) {
-            mTtlHelper.destroy();
-        } else {
-            getContext().unregisterReceiver(mNotificationTimeoutReceiver);
+        if (mIntentReceiver != null) {
+            getContext().unregisterReceiver(mIntentReceiver);
         }
-        getContext().unregisterReceiver(mRestoreReceiver);
-        getContext().unregisterReceiver(mLocaleChangeReceiver);
-
-        mSettingsObserver.destroy();
-        mRoleObserver.destroy();
+        if (mPackageIntentReceiver != null) {
+            getContext().unregisterReceiver(mPackageIntentReceiver);
+        }
+        if (Flags.allNotifsNeedTtl()) {
+            if (mTtlHelper != null) {
+                mTtlHelper.destroy();
+            }
+        } else {
+            if (mNotificationTimeoutReceiver != null) {
+                getContext().unregisterReceiver(mNotificationTimeoutReceiver);
+            }
+        }
+        if (mRestoreReceiver != null) {
+            getContext().unregisterReceiver(mRestoreReceiver);
+        }
+        if (mLocaleChangeReceiver != null) {
+            getContext().unregisterReceiver(mLocaleChangeReceiver);
+        }
+        if (mSettingsObserver != null) {
+            mSettingsObserver.destroy();
+        }
+        if (mRoleObserver != null) {
+            mRoleObserver.destroy();
+        }
         if (mShortcutHelper != null) {
             mShortcutHelper.destroy();
         }
-        mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_PREFERENCES);
-        mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
-        mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
-        mStatsManager.clearPullAtomCallback(DND_MODE_RULE);
-        mAppOps.stopWatchingMode(mAppOpsListener);
-        mAlarmManager.cancelAll();
+        if (mStatsManager != null) {
+            mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_PREFERENCES);
+            mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
+            mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
+            mStatsManager.clearPullAtomCallback(DND_MODE_RULE);
+        }
+        if (mAppOps != null) {
+            mAppOps.stopWatchingMode(mAppOpsListener);
+        }
+        if (mAlarmManager != null) {
+            mAlarmManager.cancelAll();
+        }
     }
 
     protected String[] getStringArrayResource(int key) {
@@ -5809,7 +5832,16 @@
 
         @Override
         public ComponentName getEffectsSuppressor() {
-            return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
+            ComponentName suppressor = !mEffectsSuppressors.isEmpty()
+                    ? mEffectsSuppressors.get(0)
+                    : null;
+            if (isCallerSystemOrSystemUiOrShell() || suppressor == null
+                    || mPackageManagerInternal.isSameApp(suppressor.getPackageName(),
+                    Binder.getCallingUid(), UserHandle.getUserId(Binder.getCallingUid()))) {
+                return suppressor;
+            }
+
+            return null;
         }
 
         @Override
@@ -7202,7 +7234,15 @@
                 callingUid, userId, true, false, "cancelNotificationWithTag", pkg);
 
         // ensure opPkg is delegate if does not match pkg
-        int uid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+
+        int uid = INVALID_UID;
+
+        try {
+            uid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+        } catch (NameNotFoundException e) {
+            // package either never existed so there's no posted notification or it's being
+            // uninstalled so we'll be cleaning it up soon. log and return immediately below.
+        }
 
         if (uid == INVALID_UID) {
             Slog.w(TAG, opPkg + ":" + callingUid + " trying to cancel notification "
@@ -7296,7 +7336,13 @@
 
         // Can throw a SecurityException if the calling uid doesn't have permission to post
         // as "pkg"
-        final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+        int notificationUid = INVALID_UID;
+
+        try {
+            notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+        } catch (NameNotFoundException e) {
+            // not great -  throw immediately below
+        }
 
         if (notificationUid == INVALID_UID) {
             throw new SecurityException("Caller " + opPkg + ":" + callingUid
@@ -7722,6 +7768,9 @@
             return true;
         }
         // Check if an app has been given system exemption
+        if (ai.uid == Process.SYSTEM_UID) {
+            return false;
+        }
         return mAppOps.checkOpNoThrow(
                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid,
                 ai.packageName) == MODE_ALLOWED;
@@ -7850,7 +7899,8 @@
     }
 
     @VisibleForTesting
-    int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId) {
+    int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId)
+            throws NameNotFoundException {
         if (userId == USER_ALL) {
             userId = USER_SYSTEM;
         }
@@ -7861,12 +7911,8 @@
             return callingUid;
         }
 
-        int targetUid = INVALID_UID;
-        try {
-            targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
-        } catch (NameNotFoundException e) {
-            /* ignore, handled by caller */
-        }
+        int targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
+
         // posted from app A on behalf of app B
         if (isCallerAndroid(callingPkg, callingUid)
                 || mPreferencesHelper.isDelegateAllowed(
@@ -8016,6 +8062,13 @@
             return false;
         }
 
+        if (Flags.rejectOldNotifications() && n.hasAppProvidedWhen() && n.getWhen() > 0
+                && (System.currentTimeMillis() - n.getWhen()) > NOTIFICATION_MAX_AGE_AT_POST) {
+            Slog.d(TAG, "Ignored enqueue for old " + n.getWhen() + " notification " + r.getKey());
+            mUsageStats.registerTooOldBlocked(r);
+            return false;
+        }
+
         return true;
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index e960f4b..c09077e 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -257,6 +257,14 @@
         }
     }
 
+    public synchronized void registerTooOldBlocked(NotificationRecord notification) {
+        AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
+        for (AggregatedStats stats : aggregatedStatsArray) {
+            stats.numTooOld++;
+        }
+        releaseAggregatedStatsLocked(aggregatedStatsArray);
+    }
+
     @GuardedBy("this")
     private AggregatedStats[] getAggregatedStatsLocked(NotificationRecord record) {
         return getAggregatedStatsLocked(record.getSbn().getPackageName());
@@ -405,6 +413,7 @@
         public int numUndecoratedRemoteViews;
         public long mLastAccessTime;
         public int numImagesRemoved;
+        public int numTooOld;
 
         public AggregatedStats(Context context, String key) {
             this.key = key;
@@ -535,6 +544,7 @@
             maybeCount("note_over_alert_rate", (numAlertViolations - previous.numAlertViolations));
             maybeCount("note_over_quota", (numQuotaViolations - previous.numQuotaViolations));
             maybeCount("note_images_removed", (numImagesRemoved - previous.numImagesRemoved));
+            maybeCount("not_too_old", (numTooOld - previous.numTooOld));
             noisyImportance.maybeCount(previous.noisyImportance);
             quietImportance.maybeCount(previous.quietImportance);
             finalImportance.maybeCount(previous.finalImportance);
@@ -570,6 +580,7 @@
             previous.numAlertViolations = numAlertViolations;
             previous.numQuotaViolations = numQuotaViolations;
             previous.numImagesRemoved = numImagesRemoved;
+            previous.numTooOld = numTooOld;
             noisyImportance.update(previous.noisyImportance);
             quietImportance.update(previous.quietImportance);
             finalImportance.update(previous.finalImportance);
@@ -679,6 +690,8 @@
             output.append("numQuotaViolations=").append(numQuotaViolations).append("\n");
             output.append(indentPlusTwo);
             output.append("numImagesRemoved=").append(numImagesRemoved).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numTooOld=").append(numTooOld).append("\n");
             output.append(indentPlusTwo).append(noisyImportance.toString()).append("\n");
             output.append(indentPlusTwo).append(quietImportance.toString()).append("\n");
             output.append(indentPlusTwo).append(finalImportance.toString()).append("\n");
@@ -725,6 +738,7 @@
             maybePut(dump, "notificationEnqueueRate", getEnqueueRate());
             maybePut(dump, "numAlertViolations", numAlertViolations);
             maybePut(dump, "numImagesRemoved", numImagesRemoved);
+            maybePut(dump, "numTooOld", numTooOld);
             noisyImportance.maybePut(dump, previous.noisyImportance);
             quietImportance.maybePut(dump, previous.quietImportance);
             finalImportance.maybePut(dump, previous.finalImportance);
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 9dcca49..bf6b652 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -135,3 +135,10 @@
   description: "This flag controls which signal is used to handle a user switch system event"
   bug: "337077643"
 }
+
+flag {
+  name: "reject_old_notifications"
+  namespace: "systemui"
+  description: "This flag does not allow notifications older than 2 weeks old to be posted"
+  bug: "339833083"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
index 681dd0b..96ab2cc 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
@@ -33,6 +33,7 @@
 import android.os.BadParcelableException;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
@@ -41,7 +42,10 @@
 import android.system.Os;
 import android.util.Log;
 
+import com.android.internal.infra.AndroidFuture;
+
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Util methods for ensuring the Bundle passed in various methods are read-only and restricted to
@@ -78,16 +82,16 @@
             if (canMarshall(obj) || obj instanceof CursorWindow) {
                 continue;
             }
-
-            if (obj instanceof ParcelFileDescriptor) {
+            if (obj instanceof Bundle) {
+              sanitizeInferenceParams((Bundle) obj);
+            } else if (obj instanceof ParcelFileDescriptor) {
                 validatePfdReadOnly((ParcelFileDescriptor) obj);
             } else if (obj instanceof SharedMemory) {
                 ((SharedMemory) obj).setProtect(PROT_READ);
             } else if (obj instanceof Bitmap) {
-                if (((Bitmap) obj).isMutable()) {
-                    throw new BadParcelableException(
-                            "Encountered a mutable Bitmap in the Bundle at key : " + key);
-                }
+                validateBitmap((Bitmap) obj);
+            } else if (obj instanceof Parcelable[]) {
+                validateParcelableArray((Parcelable[]) obj);
             } else {
                 throw new BadParcelableException(
                         "Unsupported Parcelable type encountered in the Bundle: "
@@ -125,20 +129,20 @@
                 continue;
             }
 
-            if (obj instanceof ParcelFileDescriptor) {
+            if (obj instanceof Bundle) {
+                sanitizeResponseParams((Bundle) obj);
+            } else if (obj instanceof ParcelFileDescriptor) {
                 validatePfdReadOnly((ParcelFileDescriptor) obj);
             } else if (obj instanceof Bitmap) {
-                if (((Bitmap) obj).isMutable()) {
-                    throw new BadParcelableException(
-                            "Encountered a mutable Bitmap in the Bundle at key : " + key);
-                }
+                validateBitmap((Bitmap) obj);
+            } else if (obj instanceof Parcelable[]) {
+                validateParcelableArray((Parcelable[]) obj);
             } else {
                 throw new BadParcelableException(
                         "Unsupported Parcelable type encountered in the Bundle: "
                                 + obj.getClass().getSimpleName());
             }
         }
-        Log.e(TAG, "validateResponseParams : Finished");
     }
 
     /**
@@ -183,7 +187,8 @@
 
     public static IStreamingResponseCallback wrapWithValidation(
             IStreamingResponseCallback streamingResponseCallback,
-            Executor resourceClosingExecutor) {
+            Executor resourceClosingExecutor,
+            AndroidFuture future) {
         return new IStreamingResponseCallback.Stub() {
             @Override
             public void onNewContent(Bundle processedResult) throws RemoteException {
@@ -203,6 +208,7 @@
                     streamingResponseCallback.onSuccess(resultBundle);
                 } finally {
                     resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+                    future.complete(null);
                 }
             }
 
@@ -210,6 +216,7 @@
             public void onFailure(int errorCode, String errorMessage,
                     PersistableBundle errorParams) throws RemoteException {
                 streamingResponseCallback.onFailure(errorCode, errorMessage, errorParams);
+                future.completeExceptionally(new TimeoutException());
             }
 
             @Override
@@ -237,7 +244,8 @@
     }
 
     public static IResponseCallback wrapWithValidation(IResponseCallback responseCallback,
-            Executor resourceClosingExecutor) {
+            Executor resourceClosingExecutor,
+            AndroidFuture future) {
         return new IResponseCallback.Stub() {
             @Override
             public void onSuccess(Bundle resultBundle)
@@ -247,6 +255,7 @@
                     responseCallback.onSuccess(resultBundle);
                 } finally {
                     resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+                    future.complete(null);
                 }
             }
 
@@ -254,6 +263,7 @@
             public void onFailure(int errorCode, String errorMessage,
                     PersistableBundle errorParams) throws RemoteException {
                 responseCallback.onFailure(errorCode, errorMessage, errorParams);
+                future.completeExceptionally(new TimeoutException());
             }
 
             @Override
@@ -280,17 +290,20 @@
     }
 
 
-    public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback) {
+    public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback,
+            AndroidFuture future) {
         return new ITokenInfoCallback.Stub() {
             @Override
             public void onSuccess(TokenInfo tokenInfo) throws RemoteException {
                 responseCallback.onSuccess(tokenInfo);
+                future.complete(null);
             }
 
             @Override
             public void onFailure(int errorCode, String errorMessage, PersistableBundle errorParams)
                     throws RemoteException {
                 responseCallback.onFailure(errorCode, errorMessage, errorParams);
+                future.completeExceptionally(new TimeoutException());
             }
         };
     }
@@ -310,6 +323,26 @@
         }
     }
 
+    private static void validateParcelableArray(Parcelable[] parcelables) {
+        if (parcelables.length > 0
+                && parcelables[0] instanceof ParcelFileDescriptor) {
+            // Safe to cast
+            validatePfdsReadOnly(parcelables);
+        } else if (parcelables.length > 0
+                && parcelables[0] instanceof Bitmap) {
+            validateBitmapsImmutable(parcelables);
+        } else {
+            throw new BadParcelableException(
+                    "Could not cast to any known parcelable array");
+        }
+    }
+
+    public static void validatePfdsReadOnly(Parcelable[] pfds) {
+        for (Parcelable pfd : pfds) {
+            validatePfdReadOnly((ParcelFileDescriptor) pfd);
+        }
+    }
+
     public static void validatePfdReadOnly(ParcelFileDescriptor pfd) {
         if (pfd == null) {
             return;
@@ -326,6 +359,19 @@
         }
     }
 
+    private static void validateBitmap(Bitmap obj) {
+        if (obj.isMutable()) {
+            throw new BadParcelableException(
+                    "Encountered a mutable Bitmap in the Bundle at key : " + obj);
+        }
+    }
+
+    private static void validateBitmapsImmutable(Parcelable[] bitmaps) {
+        for (Parcelable bitmap : bitmaps) {
+            validateBitmap((Bitmap) bitmap);
+        }
+    }
+
     public static void tryCloseResource(Bundle bundle) {
         if (bundle == null || bundle.isEmpty() || !bundle.hasFileDescriptors()) {
             return;
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index 99401a1..ac73382 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -16,6 +16,10 @@
 
 package com.android.server.ondeviceintelligence;
 
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY;
+
 import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeInferenceParams;
 import static com.android.server.ondeviceintelligence.BundleUtil.validatePfdReadOnly;
 import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeStateParams;
@@ -29,6 +33,7 @@
 import android.app.AppGlobals;
 import android.app.ondeviceintelligence.DownloadCallback;
 import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.FeatureDetails;
 import android.app.ondeviceintelligence.IDownloadCallback;
 import android.app.ondeviceintelligence.IFeatureCallback;
 import android.app.ondeviceintelligence.IFeatureDetailsCallback;
@@ -41,6 +46,7 @@
 import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
@@ -59,6 +65,7 @@
 import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
+import android.provider.Settings;
 import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
 import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
 import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
@@ -77,13 +84,17 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.ondeviceintelligence.callbacks.ListenableDownloadCallback;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * This is the system service for handling calls on the
@@ -105,12 +116,20 @@
     /** Handler message to {@link #resetTemporaryServices()} */
     private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
 
+    /** Handler message to clean up temporary broadcast keys. */
+    private static final int MSG_RESET_BROADCAST_KEYS = 1;
+
     /** Default value in absence of {@link DeviceConfig} override. */
     private static final boolean DEFAULT_SERVICE_ENABLED = true;
     private static final String NAMESPACE_ON_DEVICE_INTELLIGENCE = "ondeviceintelligence";
 
+    private static final String SYSTEM_PACKAGE = "android";
+
+
     private final Executor resourceClosingExecutor = Executors.newCachedThreadPool();
     private final Executor callbackExecutor = Executors.newCachedThreadPool();
+    private final Executor broadcastExecutor = Executors.newCachedThreadPool();
+
 
     private final Context mContext;
     protected final Object mLock = new Object();
@@ -122,12 +141,17 @@
 
     @GuardedBy("mLock")
     private String[] mTemporaryServiceNames;
+    @GuardedBy("mLock")
+    private String[] mTemporaryBroadcastKeys;
+    @GuardedBy("mLock")
+    private String mBroadcastPackageName;
 
     /**
      * Handler used to reset the temporary service names.
      */
-    @GuardedBy("mLock")
     private Handler mTemporaryHandler;
+    private final @NonNull Handler mMainHandler = new Handler(Looper.getMainLooper());
+
 
     public OnDeviceIntelligenceManagerService(Context context) {
         super(context);
@@ -187,8 +211,16 @@
                     return;
                 }
                 ensureRemoteIntelligenceServiceInitialized();
-                mRemoteOnDeviceIntelligenceService.run(
-                        service -> service.getVersion(remoteCallback));
+                mRemoteOnDeviceIntelligenceService.postAsync(
+                        service -> {
+                            AndroidFuture future = new AndroidFuture();
+                            service.getVersion(new RemoteCallback(
+                                    result -> {
+                                        remoteCallback.sendResult(result);
+                                        future.complete(null);
+                                    }));
+                            return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+                        });
             }
 
             @Override
@@ -208,8 +240,25 @@
                 }
                 ensureRemoteIntelligenceServiceInitialized();
                 int callerUid = Binder.getCallingUid();
-                mRemoteOnDeviceIntelligenceService.run(
-                        service -> service.getFeature(callerUid, id, featureCallback));
+                mRemoteOnDeviceIntelligenceService.postAsync(
+                        service -> {
+                            AndroidFuture future = new AndroidFuture();
+                            service.getFeature(callerUid, id, new IFeatureCallback.Stub() {
+                                @Override
+                                public void onSuccess(Feature result) throws RemoteException {
+                                    featureCallback.onSuccess(result);
+                                    future.complete(null);
+                                }
+
+                                @Override
+                                public void onFailure(int errorCode, String errorMessage,
+                                        PersistableBundle errorParams) throws RemoteException {
+                                    featureCallback.onFailure(errorCode, errorMessage, errorParams);
+                                    future.completeExceptionally(new TimeoutException());
+                                }
+                            });
+                            return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+                        });
             }
 
             @Override
@@ -229,9 +278,29 @@
                 }
                 ensureRemoteIntelligenceServiceInitialized();
                 int callerUid = Binder.getCallingUid();
-                mRemoteOnDeviceIntelligenceService.run(
-                        service -> service.listFeatures(callerUid,
-                                listFeaturesCallback));
+                mRemoteOnDeviceIntelligenceService.postAsync(
+                        service -> {
+                            AndroidFuture future = new AndroidFuture();
+                            service.listFeatures(callerUid,
+                                    new IListFeaturesCallback.Stub() {
+                                        @Override
+                                        public void onSuccess(List<Feature> result)
+                                                throws RemoteException {
+                                            listFeaturesCallback.onSuccess(result);
+                                            future.complete(null);
+                                        }
+
+                                        @Override
+                                        public void onFailure(int errorCode, String errorMessage,
+                                                PersistableBundle errorParams)
+                                                throws RemoteException {
+                                            listFeaturesCallback.onFailure(errorCode, errorMessage,
+                                                    errorParams);
+                                            future.completeExceptionally(new TimeoutException());
+                                        }
+                                    });
+                            return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+                        });
             }
 
             @Override
@@ -253,9 +322,29 @@
                 }
                 ensureRemoteIntelligenceServiceInitialized();
                 int callerUid = Binder.getCallingUid();
-                mRemoteOnDeviceIntelligenceService.run(
-                        service -> service.getFeatureDetails(callerUid, feature,
-                                featureDetailsCallback));
+                mRemoteOnDeviceIntelligenceService.postAsync(
+                        service -> {
+                            AndroidFuture future = new AndroidFuture();
+                            service.getFeatureDetails(callerUid, feature,
+                                    new IFeatureDetailsCallback.Stub() {
+                                        @Override
+                                        public void onSuccess(FeatureDetails result)
+                                                throws RemoteException {
+                                            future.complete(null);
+                                            featureDetailsCallback.onSuccess(result);
+                                        }
+
+                                        @Override
+                                        public void onFailure(int errorCode, String errorMessage,
+                                                PersistableBundle errorParams)
+                                                throws RemoteException {
+                                            future.completeExceptionally(null);
+                                            featureDetailsCallback.onFailure(errorCode,
+                                                    errorMessage, errorParams);
+                                        }
+                                    });
+                            return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+                        });
             }
 
             @Override
@@ -276,10 +365,20 @@
                 }
                 ensureRemoteIntelligenceServiceInitialized();
                 int callerUid = Binder.getCallingUid();
-                mRemoteOnDeviceIntelligenceService.run(
-                        service -> service.requestFeatureDownload(callerUid, feature,
-                                wrapCancellationFuture(cancellationSignalFuture),
-                                downloadCallback));
+                mRemoteOnDeviceIntelligenceService.postAsync(
+                        service -> {
+                            AndroidFuture future = new AndroidFuture();
+                            ListenableDownloadCallback listenableDownloadCallback =
+                                    new ListenableDownloadCallback(
+                                            downloadCallback,
+                                            mMainHandler, future, getIdleTimeoutMs());
+                            service.requestFeatureDownload(callerUid, feature,
+                                    wrapCancellationFuture(cancellationSignalFuture),
+                                    listenableDownloadCallback);
+                            return future; // this future has no timeout because, actual download
+                            // might take long, but we fail early if there is no progress callbacks.
+                        }
+                );
             }
 
 
@@ -306,11 +405,15 @@
                     }
                     ensureRemoteInferenceServiceInitialized();
                     int callerUid = Binder.getCallingUid();
-                    result = mRemoteInferenceService.post(
-                            service -> service.requestTokenInfo(callerUid, feature,
-                                    request,
-                                    wrapCancellationFuture(cancellationSignalFuture),
-                                    wrapWithValidation(tokenInfoCallback)));
+                    result = mRemoteInferenceService.postAsync(
+                            service -> {
+                                AndroidFuture future = new AndroidFuture();
+                                service.requestTokenInfo(callerUid, feature,
+                                        request,
+                                        wrapCancellationFuture(cancellationSignalFuture),
+                                        wrapWithValidation(tokenInfoCallback, future));
+                                return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+                            });
                     result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
                             resourceClosingExecutor);
                 } finally {
@@ -345,13 +448,18 @@
                     }
                     ensureRemoteInferenceServiceInitialized();
                     int callerUid = Binder.getCallingUid();
-                    result = mRemoteInferenceService.post(
-                            service -> service.processRequest(callerUid, feature,
-                                    request,
-                                    requestType,
-                                    wrapCancellationFuture(cancellationSignalFuture),
-                                    wrapProcessingFuture(processingSignalFuture),
-                                    wrapWithValidation(responseCallback, resourceClosingExecutor)));
+                    result = mRemoteInferenceService.postAsync(
+                            service -> {
+                                AndroidFuture future = new AndroidFuture();
+                                service.processRequest(callerUid, feature,
+                                        request,
+                                        requestType,
+                                        wrapCancellationFuture(cancellationSignalFuture),
+                                        wrapProcessingFuture(processingSignalFuture),
+                                        wrapWithValidation(responseCallback,
+                                                resourceClosingExecutor, future));
+                                return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+                            });
                     result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
                             resourceClosingExecutor);
                 } finally {
@@ -385,13 +493,18 @@
                     }
                     ensureRemoteInferenceServiceInitialized();
                     int callerUid = Binder.getCallingUid();
-                    result = mRemoteInferenceService.post(
-                            service -> service.processRequestStreaming(callerUid,
-                                    feature,
-                                    request, requestType,
-                                    wrapCancellationFuture(cancellationSignalFuture),
-                                    wrapProcessingFuture(processingSignalFuture),
-                                    streamingCallback));
+                    result = mRemoteInferenceService.postAsync(
+                            service -> {
+                                AndroidFuture future = new AndroidFuture();
+                                service.processRequestStreaming(callerUid,
+                                        feature,
+                                        request, requestType,
+                                        wrapCancellationFuture(cancellationSignalFuture),
+                                        wrapProcessingFuture(processingSignalFuture),
+                                        wrapWithValidation(streamingCallback,
+                                                resourceClosingExecutor, future));
+                                return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+                            });
                     result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
                             resourceClosingExecutor);
                 } finally {
@@ -482,6 +595,8 @@
                                     ensureRemoteIntelligenceServiceInitialized();
                                     mRemoteOnDeviceIntelligenceService.run(
                                             IOnDeviceIntelligenceService::notifyInferenceServiceConnected);
+                                    broadcastExecutor.execute(
+                                            () -> registerModelLoadingBroadcasts(service));
                                     service.registerRemoteStorageService(
                                             getIRemoteStorageService());
                                 } catch (RemoteException ex) {
@@ -493,6 +608,56 @@
         }
     }
 
+    private void registerModelLoadingBroadcasts(IOnDeviceSandboxedInferenceService service) {
+        String[] modelBroadcastKeys;
+        try {
+            modelBroadcastKeys = getBroadcastKeys();
+        } catch (Resources.NotFoundException e) {
+            Slog.d(TAG, "Skipping model broadcasts as broadcast intents configured.");
+            return;
+        }
+
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY, true);
+        try {
+            service.updateProcessingState(bundle, new IProcessingUpdateStatusCallback.Stub() {
+                @Override
+                public void onSuccess(PersistableBundle statusParams) {
+                    Binder.clearCallingIdentity();
+                    synchronized (mLock) {
+                        if (statusParams.containsKey(MODEL_LOADED_BUNDLE_KEY)) {
+                            String modelLoadedBroadcastKey = modelBroadcastKeys[0];
+                            if (modelLoadedBroadcastKey != null
+                                    && !modelLoadedBroadcastKey.isEmpty()) {
+                                final Intent intent = new Intent(modelLoadedBroadcastKey);
+                                intent.setPackage(mBroadcastPackageName);
+                                mContext.sendBroadcast(intent,
+                                        Manifest.permission.USE_ON_DEVICE_INTELLIGENCE);
+                            }
+                        } else if (statusParams.containsKey(MODEL_UNLOADED_BUNDLE_KEY)) {
+                            String modelUnloadedBroadcastKey = modelBroadcastKeys[1];
+                            if (modelUnloadedBroadcastKey != null
+                                    && !modelUnloadedBroadcastKey.isEmpty()) {
+                                final Intent intent = new Intent(modelUnloadedBroadcastKey);
+                                intent.setPackage(mBroadcastPackageName);
+                                mContext.sendBroadcast(intent,
+                                        Manifest.permission.USE_ON_DEVICE_INTELLIGENCE);
+                            }
+                        }
+                    }
+                }
+
+                @Override
+                public void onFailure(int errorCode, String errorMessage) {
+                    Slog.e(TAG, "Failed to register model loading callback with status code",
+                            new OnDeviceIntelligenceException(errorCode, errorMessage));
+                }
+            });
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to register model loading callback with status code", e);
+        }
+    }
+
     @NonNull
     private IRemoteStorageService.Stub getIRemoteStorageService() {
         return new IRemoteStorageService.Stub() {
@@ -629,6 +794,20 @@
                         R.string.config_defaultOnDeviceSandboxedInferenceService)};
     }
 
+    protected String[] getBroadcastKeys() throws Resources.NotFoundException {
+        // TODO 329240495 : Consider a small class with explicit field names for the two services
+        synchronized (mLock) {
+            if (mTemporaryBroadcastKeys != null && mTemporaryBroadcastKeys.length == 2) {
+                return mTemporaryBroadcastKeys;
+            }
+        }
+
+        return new String[]{mContext.getResources().getString(
+                R.string.config_onDeviceIntelligenceModelLoadedBroadcastKey),
+                mContext.getResources().getString(
+                        R.string.config_onDeviceIntelligenceModelUnloadedBroadcastKey)};
+    }
+
     @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
     public void setTemporaryServices(@NonNull String[] componentNames, int durationMs) {
         Objects.requireNonNull(componentNames);
@@ -645,32 +824,32 @@
                 mRemoteOnDeviceIntelligenceService.unbind();
                 mRemoteOnDeviceIntelligenceService = null;
             }
-            if (mTemporaryHandler == null) {
-                mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
-                    @Override
-                    public void handleMessage(Message msg) {
-                        if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
-                            synchronized (mLock) {
-                                resetTemporaryServices();
-                            }
-                        } else {
-                            Slog.wtf(TAG, "invalid handler msg: " + msg);
-                        }
-                    }
-                };
-            } else {
-                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
-            }
 
             if (durationMs != -1) {
-                mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
+                getTemporaryHandler().sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE,
+                        durationMs);
+            }
+        }
+    }
+
+    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    public void setModelBroadcastKeys(@NonNull String[] broadcastKeys, String receiverPackageName,
+            int durationMs) {
+        Objects.requireNonNull(broadcastKeys);
+        enforceShellOnly(Binder.getCallingUid(), "setModelBroadcastKeys");
+        mContext.enforceCallingPermission(
+                Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+        synchronized (mLock) {
+            mTemporaryBroadcastKeys = broadcastKeys;
+            mBroadcastPackageName = receiverPackageName;
+            if (durationMs != -1) {
+                getTemporaryHandler().sendEmptyMessageDelayed(MSG_RESET_BROADCAST_KEYS, durationMs);
             }
         }
     }
 
     @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
     public void resetTemporaryServices() {
-        enforceShellOnly(Binder.getCallingUid(), "resetTemporaryServices");
         mContext.enforceCallingPermission(
                 Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
         synchronized (mLock) {
@@ -751,4 +930,34 @@
             }
         }
     }
+
+    private synchronized Handler getTemporaryHandler() {
+        if (mTemporaryHandler == null) {
+            mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+                @Override
+                public void handleMessage(Message msg) {
+                    if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
+                        synchronized (mLock) {
+                            resetTemporaryServices();
+                        }
+                    } else if (msg.what == MSG_RESET_BROADCAST_KEYS) {
+                        synchronized (mLock) {
+                            mTemporaryBroadcastKeys = null;
+                            mBroadcastPackageName = SYSTEM_PACKAGE;
+                        }
+                    } else {
+                        Slog.wtf(TAG, "invalid handler msg: " + msg);
+                    }
+                }
+            };
+        }
+
+        return mTemporaryHandler;
+    }
+
+    private long getIdleTimeoutMs() {
+        return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+                Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, TimeUnit.HOURS.toMillis(1),
+                mContext.getUserId());
+    }
 }
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
index a76d8a3..b52812f 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
@@ -17,6 +17,7 @@
 package com.android.server.ondeviceintelligence;
 
 import android.annotation.NonNull;
+import android.os.Binder;
 import android.os.ShellCommand;
 
 import java.io.PrintWriter;
@@ -43,6 +44,8 @@
                 return setTemporaryServices();
             case "get-services":
                 return getConfiguredServices();
+            case "set-model-broadcasts":
+                return setBroadcastKeys();
             default:
                 return handleDefaultCommands(cmd);
         }
@@ -62,14 +65,22 @@
         pw.println("    To reset, call without any arguments.");
 
         pw.println("  get-services To get the names of services that are currently being used.");
+        pw.println(
+                "  set-model-broadcasts [ModelLoadedBroadcastKey] [ModelUnloadedBroadcastKey] "
+                        + "[ReceiverPackageName] "
+                        + "[DURATION] To set the names of broadcast intent keys that are to be "
+                        + "emitted for cts tests.");
     }
 
     private int setTemporaryServices() {
         final PrintWriter out = getOutPrintWriter();
         final String intelligenceServiceName = getNextArg();
         final String inferenceServiceName = getNextArg();
+
         if (getRemainingArgsCount() == 0 && intelligenceServiceName == null
                 && inferenceServiceName == null) {
+            OnDeviceIntelligenceManagerService.enforceShellOnly(Binder.getCallingUid(),
+                    "resetTemporaryServices");
             mService.resetTemporaryServices();
             out.println("OnDeviceIntelligenceManagerService temporary reset. ");
             return 0;
@@ -79,7 +90,8 @@
         Objects.requireNonNull(inferenceServiceName);
         final int duration = Integer.parseInt(getNextArgRequired());
         mService.setTemporaryServices(
-                new String[]{intelligenceServiceName, inferenceServiceName}, duration);
+                new String[]{intelligenceServiceName, inferenceServiceName},
+                duration);
         out.println("OnDeviceIntelligenceService temporarily set to " + intelligenceServiceName
                 + " \n and \n OnDeviceTrustedInferenceService set to " + inferenceServiceName
                 + " for " + duration + "ms");
@@ -93,4 +105,22 @@
                 + " \n and \n OnDeviceTrustedInferenceService set to : " + services[1]);
         return 0;
     }
+
+    private int setBroadcastKeys() {
+        final PrintWriter out = getOutPrintWriter();
+        final String modelLoadedKey = getNextArgRequired();
+        final String modelUnloadedKey = getNextArgRequired();
+        final String receiverPackageName = getNextArg();
+
+        final int duration = Integer.parseInt(getNextArgRequired());
+        mService.setModelBroadcastKeys(
+                new String[]{modelLoadedKey, modelUnloadedKey}, receiverPackageName, duration);
+        out.println("OnDeviceIntelligence Model Loading broadcast keys temporarily set to "
+                + modelLoadedKey
+                + " \n and \n OnDeviceTrustedInferenceService set to " + modelUnloadedKey
+                + "\n and Package name set to : " + receiverPackageName
+                + " for " + duration + "ms");
+        return 0;
+    }
+
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
index 48258d7..ac9747a 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
@@ -22,17 +22,21 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.provider.Settings;
 import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
 import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
 
 import com.android.internal.infra.ServiceConnector;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * Manages the connection to the remote on-device intelligence service. Also, handles unbinding
  * logic set by the service implementation via a Secure Settings flag.
  */
 public class RemoteOnDeviceIntelligenceService extends
         ServiceConnector.Impl<IOnDeviceIntelligenceService> {
+    private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(4);
     private static final String TAG =
             RemoteOnDeviceIntelligenceService.class.getSimpleName();
 
@@ -48,9 +52,15 @@
     }
 
     @Override
+    protected long getRequestTimeoutMs() {
+        return LONG_TIMEOUT;
+    }
+
+    @Override
     protected long getAutoDisconnectTimeoutMs() {
-        // Disable automatic unbinding.
-        // TODO: add logic to fetch this flag via SecureSettings.
-        return -1;
+        return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+                Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
+                TimeUnit.SECONDS.toMillis(30),
+                mContext.getUserId());
     }
 }
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
index 69ba1d2..18b1383 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
@@ -22,18 +22,24 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.provider.Settings;
 import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
 import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
 
 import com.android.internal.infra.ServiceConnector;
 
+import java.util.concurrent.TimeUnit;
+
 
 /**
- * Manages the connection to the remote on-device sand boxed inference service. Also, handles unbinding
+ * Manages the connection to the remote on-device sand boxed inference service. Also, handles
+ * unbinding
  * logic set by the service implementation via a SecureSettings flag.
  */
 public class RemoteOnDeviceSandboxedInferenceService extends
         ServiceConnector.Impl<IOnDeviceSandboxedInferenceService> {
+    private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(1);
+
     /**
      * Creates an instance of {@link ServiceConnector}
      *
@@ -54,11 +60,17 @@
         connect();
     }
 
+    @Override
+    protected long getRequestTimeoutMs() {
+        return LONG_TIMEOUT;
+    }
+
 
     @Override
     protected long getAutoDisconnectTimeoutMs() {
-        // Disable automatic unbinding.
-        // TODO: add logic to fetch this flag via SecureSettings.
-        return -1;
+        return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+                Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
+                TimeUnit.SECONDS.toMillis(30),
+                mContext.getUserId());
     }
 }
diff --git a/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java b/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
new file mode 100644
index 0000000..32f0698
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence.callbacks;
+
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This class extends the {@link IDownloadCallback} and adds a timeout Runnable to the callback
+ * such that, in the case where the callback methods are not invoked, we do not have to wait for
+ * timeout based on {@link #onDownloadCompleted} which might take minutes or hours to complete in
+ * some cases. Instead, in such cases we rely on the remote service sending progress updates and if
+ * there are *no* progress callbacks in the duration of {@link #idleTimeoutMs}, we can assume the
+ * download will not complete and enabling faster cleanup.
+ */
+public class ListenableDownloadCallback extends IDownloadCallback.Stub implements Runnable {
+    private final IDownloadCallback callback;
+    private final Handler handler;
+    private final AndroidFuture future;
+    private final long idleTimeoutMs;
+
+    /**
+     * Constructor to create a ListenableDownloadCallback.
+     *
+     * @param callback      callback to send download updates to caller.
+     * @param handler       handler to schedule timeout runnable.
+     * @param future        future to complete to signal the callback has reached a terminal state.
+     * @param idleTimeoutMs timeout within which download updates should be received.
+     */
+    public ListenableDownloadCallback(IDownloadCallback callback, Handler handler,
+            AndroidFuture future,
+            long idleTimeoutMs) {
+        this.callback = callback;
+        this.handler = handler;
+        this.future = future;
+        this.idleTimeoutMs = idleTimeoutMs;
+        handler.postDelayed(this,
+                idleTimeoutMs); // init the timeout runnable in case no callback is ever invoked
+    }
+
+    @Override
+    public void onDownloadStarted(long bytesToDownload) throws RemoteException {
+        callback.onDownloadStarted(bytesToDownload);
+        handler.removeCallbacks(this);
+        handler.postDelayed(this, idleTimeoutMs);
+    }
+
+    @Override
+    public void onDownloadProgress(long bytesDownloaded) throws RemoteException {
+        callback.onDownloadProgress(bytesDownloaded);
+        handler.removeCallbacks(this); // remove previously queued timeout tasks.
+        handler.postDelayed(this, idleTimeoutMs); // queue fresh timeout task for next update.
+    }
+
+    @Override
+    public void onDownloadFailed(int failureStatus,
+            String errorMessage, PersistableBundle errorParams) throws RemoteException {
+        callback.onDownloadFailed(failureStatus, errorMessage, errorParams);
+        handler.removeCallbacks(this);
+        future.completeExceptionally(new TimeoutException());
+    }
+
+    @Override
+    public void onDownloadCompleted(
+            android.os.PersistableBundle downloadParams) throws RemoteException {
+        callback.onDownloadCompleted(downloadParams);
+        handler.removeCallbacks(this);
+        future.complete(null);
+    }
+
+    @Override
+    public void run() {
+        future.completeExceptionally(
+                new TimeoutException()); // complete the future as we haven't received updates
+        // for download progress.
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 9ba88aa..fe774aa 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -504,9 +504,12 @@
         } else {
             storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
         }
-        List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
-                UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
-                true /* onlyCoreApps */);
+        final List<String> deferPackages;
+        synchronized (mPm.mInstallLock) {
+           deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
+                    UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
+                    true /* onlyCoreApps */);
+        }
         Future<?> prepareAppDataFuture = SystemServerInitThreadPool.submit(() -> {
             TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",
                     Trace.TRACE_TAG_PACKAGE_MANAGER);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 19a0ba7..472f228 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -37,7 +37,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_STAGED;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL_PATH;
+import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
 import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
 import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
@@ -505,6 +505,7 @@
         // metadata file path for the new package.
         if (oldPkgSetting != null) {
             pkgSetting.setAppMetadataFilePath(null);
+            pkgSetting.setAppMetadataSource(APP_METADATA_SOURCE_UNKNOWN);
         }
         // If the app metadata file path is not null then this is a system app with a preloaded app
         // metadata file on the system image. Do not reset the path and source if this is the
@@ -523,7 +524,7 @@
                 }
             } else if (Flags.aslInApkAppMetadataSource()) {
                 Map<String, PackageManager.Property> properties = pkg.getProperties();
-                if (properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL_PATH)) {
+                if (properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL)) {
                     // ASL file extraction is done in post-install
                     pkgSetting.setAppMetadataFilePath(appMetadataFile.getAbsolutePath());
                     pkgSetting.setAppMetadataSource(APP_METADATA_SOURCE_APK);
@@ -985,13 +986,13 @@
     }
 
     void installPackagesTraced(List<InstallRequest> requests) {
-        synchronized (mPm.mInstallLock) {
-            try {
-                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
-                installPackagesLI(requests);
-            } finally {
-                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-            }
+        mPm.mInstallLock.lock();
+        try {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
+            installPackagesLI(requests);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+            mPm.mInstallLock.unlock();
         }
     }
 
@@ -2590,22 +2591,30 @@
             final boolean performDexopt = DexOptHelper.shouldPerformDexopt(installRequest,
                     dexoptOptions, mContext);
             if (performDexopt) {
-                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+                // dexopt can take long, and ArtService doesn't require installd, so we release
+                // the lock here and re-acquire the lock after dexopt is finished.
+                mPm.mInstallLock.unlock();
+                try {
+                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
 
-                // This mirrors logic from commitReconciledScanResultLocked, where the library files
-                // needed for dexopt are assigned.
-                PackageSetting realPkgSetting = installRequest.getRealPackageSetting();
+                    // This mirrors logic from commitReconciledScanResultLocked, where the library
+                    // files needed for dexopt are assigned.
+                    PackageSetting realPkgSetting = installRequest.getRealPackageSetting();
 
-                // Unfortunately, the updated system app flag is only tracked on this PackageSetting
-                boolean isUpdatedSystemApp =
-                        installRequest.getScannedPackageSetting().isUpdatedSystemApp();
+                    // Unfortunately, the updated system app flag is only tracked on this
+                    // PackageSetting
+                    boolean isUpdatedSystemApp =
+                            installRequest.getScannedPackageSetting().isUpdatedSystemApp();
 
-                realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
+                    realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
 
-                DexoptResult dexOptResult =
-                        DexOptHelper.dexoptPackageUsingArtService(installRequest, dexoptOptions);
-                installRequest.onDexoptFinished(dexOptResult);
-                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                    DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
+                            installRequest, dexoptOptions);
+                    installRequest.onDexoptFinished(dexOptResult);
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                } finally {
+                    mPm.mInstallLock.lock();
+                }
             }
         }
         PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ae485ed..fda8535 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -626,7 +626,7 @@
     // Lock for state used when installing and doing other long running
     // operations.  Methods that must be called with this lock held have
     // the suffix "LI".
-    final Object mInstallLock;
+    final PackageManagerTracedLock mInstallLock;
 
     // ----------------------------------------------------------------
 
@@ -1692,8 +1692,8 @@
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
                 Trace.TRACE_TAG_PACKAGE_MANAGER);
         t.traceBegin("create package manager");
-        final PackageManagerTracedLock lock = new PackageManagerTracedLock();
-        final Object installLock = new Object();
+        final PackageManagerTracedLock lock = new PackageManagerTracedLock("mLock");
+        final PackageManagerTracedLock installLock = new PackageManagerTracedLock("mInstallLock");
 
         HandlerThread backgroundThread = new ServiceThread("PackageManagerBg",
                 Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
@@ -4003,7 +4003,7 @@
                 final PackageMetrics.ComponentStateMetrics componentStateMetrics =
                         new PackageMetrics.ComponentStateMetrics(setting,
                                 UserHandle.getUid(userId, packageSetting.getAppId()),
-                                packageSetting.getEnabled(userId));
+                                packageSetting.getEnabled(userId), callingUid);
                 if (!setEnabledSettingInternalLocked(computer, packageSetting, setting, userId,
                         callingPackage)) {
                     continue;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 83f3b16..ae2eaeb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -86,7 +86,7 @@
     private final Context mContext;
     private final PackageManagerTracedLock mLock;
     private final Installer mInstaller;
-    private final Object mInstallLock;
+    private final PackageManagerTracedLock mInstallLock;
     private final Handler mBackgroundHandler;
     private final Executor mBackgroundExecutor;
     private final List<ScanPartition> mSystemPartitions;
@@ -144,7 +144,7 @@
     private final Singleton<PackageMonitorCallbackHelper> mPackageMonitorCallbackHelper;
 
     PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock,
-            Installer installer, Object installLock, PackageAbiHelper abiHelper,
+            Installer installer, PackageManagerTracedLock installLock, PackageAbiHelper abiHelper,
             Handler backgroundHandler,
             List<ScanPartition> systemPartitions,
             Producer<ComponentResolver> componentResolverProducer,
@@ -254,7 +254,7 @@
         return mAbiHelper;
     }
 
-    public Object getInstallLock() {
+    public PackageManagerTracedLock getInstallLock() {
         return mInstallLock;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index b369f03..23ae983 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -19,7 +19,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL_PATH;
+import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL;
 import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDWR;
@@ -71,8 +71,12 @@
 import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
 import android.os.Binder;
 import android.os.Build;
+import android.os.CancellationSignal;
 import android.os.Debug;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -93,6 +97,7 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Base64;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.LogPrinter;
 import android.util.Printer;
@@ -147,11 +152,10 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.zip.GZIPInputStream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
 
 /**
  * Class containing helper methods for the PackageManagerService.
@@ -1668,11 +1672,11 @@
             return true;
         }
         Map<String, Property> properties = pkg.getProperties();
-        if (!properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL_PATH)) {
+        if (!properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL)) {
             return false;
         }
-        Property fileInAPkPathProperty = properties.get(PROPERTY_ANDROID_SAFETY_LABEL_PATH);
-        if (!fileInAPkPathProperty.isString()) {
+        Property fileInApkProperty = properties.get(PROPERTY_ANDROID_SAFETY_LABEL);
+        if (!fileInApkProperty.isResourceId()) {
             return false;
         }
         if (isSystem && !appMetadataFile.getParentFile().exists()) {
@@ -1684,28 +1688,46 @@
                 return false;
             }
         }
-        String fileInApkPath = fileInAPkPathProperty.getString();
         List<AndroidPackageSplit> splits = pkg.getSplits();
+        AssetManager.Builder builder = new AssetManager.Builder();
         for (int i = 0; i < splits.size(); i++) {
-            try (ZipFile zipFile = new ZipFile(splits.get(i).getPath())) {
-                ZipEntry zipEntry = zipFile.getEntry(fileInApkPath);
-                if (zipEntry != null
-                        && (isSystem || zipEntry.getSize() <= getAppMetadataSizeLimit())) {
-                    try (InputStream in = zipFile.getInputStream(zipEntry)) {
-                        try (FileOutputStream out = new FileOutputStream(appMetadataFile)) {
-                            FileUtils.copy(in, out);
-                            Os.chmod(appMetadataFile.getAbsolutePath(),
-                                    APP_METADATA_FILE_ACCESS_MODE);
-                            return true;
+            try {
+                builder.addApkAssets(ApkAssets.loadFromPath(splits.get(i).getPath()));
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to load resources from APK " + splits.get(i).getPath());
+            }
+        }
+        AssetManager assetManager = builder.build();
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        displayMetrics.setToDefaults();
+        Resources res = new Resources(assetManager, displayMetrics, null);
+        AtomicBoolean copyFailed = new AtomicBoolean(false);
+        try (InputStream in = res.openRawResource(fileInApkProperty.getResourceId())) {
+            try (FileOutputStream out = new FileOutputStream(appMetadataFile)) {
+                if (isSystem) {
+                    FileUtils.copy(in, out);
+                } else {
+                    long sizeLimit = getAppMetadataSizeLimit();
+                    CancellationSignal signal = new CancellationSignal();
+                    FileUtils.copy(in, out, signal, Runnable::run, (long progress) -> {
+                        if (progress > sizeLimit) {
+                            copyFailed.set(true);
+                            signal.cancel();
                         }
-                    }
+                    });
                 }
-            } catch (Exception e) {
-                Slog.e(TAG, e.getMessage());
+                Os.chmod(appMetadataFile.getAbsolutePath(),
+                        APP_METADATA_FILE_ACCESS_MODE);
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, e.getMessage());
+            copyFailed.set(true);
+        } finally {
+            if (copyFailed.get()) {
                 appMetadataFile.delete();
             }
         }
-        return false;
+        return !copyFailed.get();
     }
 
     public static void linkFilesToOldDirs(@NonNull Installer installer,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7a36f6d..0a8b2b2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -328,6 +328,8 @@
                     return runGetPrivappDenyPermissions();
                 case "get-oem-permissions":
                     return runGetOemPermissions();
+                case "get-signature-permission-allowlist":
+                    return runGetSignaturePermissionAllowlist();
                 case "trim-caches":
                     return runTrimCaches();
                 case "create-user":
@@ -2920,6 +2922,54 @@
         return 0;
     }
 
+    private int runGetSignaturePermissionAllowlist() {
+        final var partition = getNextArg();
+        if (partition == null) {
+            getErrPrintWriter().println("Error: no partition specified.");
+            return 1;
+        }
+        final var permissionAllowlist =
+                SystemConfig.getInstance().getPermissionAllowlist();
+        final ArrayMap<String, ArrayMap<String, Boolean>> allowlist;
+        switch (partition) {
+            case "system":
+                allowlist = permissionAllowlist.getSignatureAppAllowlist();
+                break;
+            case "vendor":
+                allowlist = permissionAllowlist.getVendorSignatureAppAllowlist();
+                break;
+            case "product":
+                allowlist = permissionAllowlist.getProductSignatureAppAllowlist();
+                break;
+            case "system-ext":
+                allowlist = permissionAllowlist.getSystemExtSignatureAppAllowlist();
+                break;
+            default:
+                getErrPrintWriter().println("Error: unknown partition: " + partition);
+                return 1;
+        }
+        final var ipw = new IndentingPrintWriter(getOutPrintWriter(), "  ");
+        final var allowlistSize = allowlist.size();
+        for (var allowlistIndex = 0; allowlistIndex < allowlistSize; allowlistIndex++) {
+            final var packageName = allowlist.keyAt(allowlistIndex);
+            final var permissions = allowlist.valueAt(allowlistIndex);
+            ipw.print("Package: ");
+            ipw.println(packageName);
+            ipw.increaseIndent();
+            final var permissionsSize = permissions.size();
+            for (var permissionsIndex = 0; permissionsIndex < permissionsSize; permissionsIndex++) {
+                final var permissionName = permissions.keyAt(permissionsIndex);
+                final var granted = permissions.valueAt(permissionsIndex);
+                if (granted) {
+                    ipw.print("Permission: ");
+                    ipw.println(permissionName);
+                }
+            }
+            ipw.decreaseIndent();
+        }
+        return 0;
+    }
+
     private int runTrimCaches() throws RemoteException {
         String size = getNextArg();
         if (size == null) {
@@ -4852,6 +4902,10 @@
         pw.println("  get-oem-permissions TARGET-PACKAGE");
         pw.println("    Prints all OEM permissions for a package.");
         pw.println("");
+        pw.println("  get-signature-permission-allowlist PARTITION");
+        pw.println("    Prints the signature permission allowlist for a partition.");
+        pw.println("    PARTITION is one of system, vendor, product and system-ext");
+        pw.println("");
         pw.println("  trim-caches DESIRED_FREE_SPACE [internal|UUID]");
         pw.println("    Trim cache files to reach the given free space.");
         pw.println("");
diff --git a/services/core/java/com/android/server/pm/PackageManagerTracedLock.java b/services/core/java/com/android/server/pm/PackageManagerTracedLock.java
index 75e1803f..303b8b9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerTracedLock.java
+++ b/services/core/java/com/android/server/pm/PackageManagerTracedLock.java
@@ -16,6 +16,9 @@
 
 package com.android.server.pm;
 
+import android.annotation.Nullable;
+import android.util.Slog;
+
 import java.util.concurrent.locks.ReentrantLock;
 
 /**
@@ -23,4 +26,31 @@
  * injection, similar to {@link ActivityManagerGlobalLock}.
  */
 public class PackageManagerTracedLock extends ReentrantLock {
+    private static final String TAG = "PackageManagerTracedLock";
+    private static final boolean DEBUG = false;
+    @Nullable private final String mLockName;
+
+    public PackageManagerTracedLock(@Nullable String lockName) {
+        mLockName = lockName;
+    }
+
+    public PackageManagerTracedLock() {
+        this(null);
+    }
+
+    @Override
+    public void lock() {
+        super.lock();
+        if (DEBUG && mLockName != null) {
+            Slog.i(TAG, "locked " + mLockName);
+        }
+    }
+
+    @Override
+    public void unlock() {
+        super.unlock();
+        if (DEBUG && mLockName != null) {
+            Slog.i(TAG, "unlocked " + mLockName);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 20598f9..2081f73 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -358,6 +358,7 @@
 
     public static class ComponentStateMetrics {
         public int mUid;
+        public int mCallingUid;
         public int mComponentOldState;
         public int mComponentNewState;
         public boolean mIsForWholeApp;
@@ -365,13 +366,14 @@
         @Nullable private String mClassName;
 
         ComponentStateMetrics(@NonNull PackageManager.ComponentEnabledSetting setting, int uid,
-                int componentOldState) {
+                int componentOldState, int callingUid) {
             mUid = uid;
             mComponentOldState = componentOldState;
             mComponentNewState = setting.getEnabledState();
             mIsForWholeApp = !setting.isComponent();
             mPackageName = setting.getPackageName();
             mClassName = setting.getClassName();
+            mCallingUid = callingUid;
         }
 
         public boolean isSameComponent(ActivityInfo activityInfo) {
@@ -412,14 +414,15 @@
                     componentStateMetrics.mComponentOldState,
                     componentStateMetrics.mComponentNewState,
                     isLauncher,
-                    componentStateMetrics.mIsForWholeApp);
+                    componentStateMetrics.mIsForWholeApp,
+                    componentStateMetrics.mCallingUid);
         }
     }
 
     private static void reportComponentStateChanged(int uid, int componentOldState,
-            int componentNewState, boolean isLauncher, boolean isForWholeApp) {
+            int componentNewState, boolean isLauncher, boolean isForWholeApp, int callingUid) {
         FrameworkStatsLog.write(FrameworkStatsLog.COMPONENT_STATE_CHANGED_REPORTED,
-                uid, componentOldState, componentNewState, isLauncher, isForWholeApp);
+                uid, componentOldState, componentNewState, isLauncher, isForWholeApp, callingUid);
     }
 
     private static List<ResolveInfo> getHomeActivitiesResolveInfoAsUser(@NonNull Computer computer,
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 3a0f7fb..02bd3cc 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -376,11 +376,14 @@
             @NonNull PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
         String packageName = deletedPs.getPackageName();
         if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
+        final boolean shouldDeletePackageSetting =
+                shouldDeletePackageSetting(deletedPs, targetUserId, allUserHandles, flags);
         // Retrieve object to delete permissions for shared user later on
         final AndroidPackage deletedPkg = deletedPs.getPkg();
 
         // Delete all the data and states related to this package.
-        clearPackageStateForUserLIF(deletedPs, targetUserId, flags);
+        clearPackageStateForUserLIF(deletedPs,
+                shouldDeletePackageSetting ? UserHandle.USER_ALL : targetUserId, flags);
 
         // Delete from mPackages
         removePackageLI(packageName, (flags & PackageManager.DELETE_CHATTY) != 0);
@@ -392,7 +395,7 @@
             deletedPs.setPkg(null);
         }
 
-        if (shouldDeletePackageSetting(deletedPs, targetUserId, allUserHandles, flags)) {
+        if (shouldDeletePackageSetting) {
             // Delete from mSettings
             final SparseBooleanArray changedUsers = new SparseBooleanArray();
             synchronized (mPm.mLock) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 41d6288..8d6d774 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2142,7 +2142,8 @@
                 ComponentName unflattenOriginalComponentName = ComponentName.unflattenFromString(
                         originalComponentName);
                 if (unflattenOriginalComponentName == null) {
-                    Slog.d(TAG, "Incorrect component name from the attributes");
+                    Slog.wtf(TAG, "Incorrect component name: " + originalComponentName
+                            + " from the attributes");
                     continue;
                 }
 
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
index 1d41401..ef32485 100644
--- a/services/core/java/com/android/server/pm/UserDataPreparer.java
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -52,11 +52,11 @@
     private static final String TAG = "UserDataPreparer";
     private static final String XATTR_SERIAL = "user.serial";
 
-    private final Object mInstallLock;
+    private final PackageManagerTracedLock mInstallLock;
     private final Context mContext;
     private final Installer mInstaller;
 
-    UserDataPreparer(Installer installer, Object installLock, Context context) {
+    UserDataPreparer(Installer installer, PackageManagerTracedLock installLock, Context context) {
         mInstallLock = installLock;
         mContext = context;
         mInstaller = installer;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f6487ce..ebdca5b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -600,8 +600,11 @@
         public void onReceive(Context context, Intent intent) {
             if (isAutoLockForPrivateSpaceEnabled()) {
                 if (ACTION_SCREEN_OFF.equals(intent.getAction())) {
+                    Slog.d(LOG_TAG, "SCREEN_OFF broadcast received");
                     maybeScheduleMessageToAutoLockPrivateSpace();
                 } else if (ACTION_SCREEN_ON.equals(intent.getAction())) {
+                    Slog.d(LOG_TAG, "SCREEN_ON broadcast received, "
+                            + "removing queued message to auto-lock private space");
                     // Remove any queued messages since the device is interactive again
                     mHandler.removeCallbacksAndMessages(PRIVATE_SPACE_AUTO_LOCK_MESSAGE_TOKEN);
                 }
@@ -619,6 +622,8 @@
                         getMainUserIdUnchecked());
         if (privateSpaceAutoLockPreference
                 != Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY) {
+            Slogf.d(LOG_TAG, "Not scheduling auto-lock on inactivity,"
+                    + "preference is set to %d", privateSpaceAutoLockPreference);
             return;
         }
         int privateProfileUserId = getPrivateProfileUserId();
@@ -632,6 +637,7 @@
     @VisibleForTesting
     void scheduleMessageToAutoLockPrivateSpace(int userId, Object token,
             long delayInMillis) {
+        Slog.i(LOG_TAG, "Scheduling auto-lock message");
         mHandler.postDelayed(() -> {
             final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
             if (powerManager != null && !powerManager.isInteractive()) {
@@ -1060,8 +1066,6 @@
         if (isAutoLockingPrivateSpaceOnRestartsEnabled()) {
             autoLockPrivateSpace();
         }
-
-        markEphemeralUsersForRemoval();
     }
 
     private boolean isAutoLockingPrivateSpaceOnRestartsEnabled() {
@@ -1098,21 +1102,6 @@
         }
     }
 
-    /** Marks all ephemeral users as slated for deletion. **/
-    private void markEphemeralUsersForRemoval() {
-        synchronized (mUsersLock) {
-            final int userSize = mUsers.size();
-            for (int i = 0; i < userSize; i++) {
-                final UserInfo ui = mUsers.valueAt(i).info;
-                if (ui.isEphemeral() && !ui.preCreated && ui.id != UserHandle.USER_SYSTEM) {
-                    addRemovingUserIdLocked(ui.id);
-                    ui.partial = true;
-                    ui.flags |= UserInfo.FLAG_DISABLED;
-                }
-            }
-        }
-    }
-
     /* Prunes out any partially created or partially removed users. */
     private void cleanupPartialUsers() {
         ArrayList<UserInfo> partials = new ArrayList<>();
@@ -1371,7 +1360,7 @@
             for (int i = 0; i < userSize; i++) {
                 UserInfo ui = mUsers.valueAt(i).info;
                 if ((excludePartial && ui.partial)
-                        || (excludeDying && isDyingLU(ui))
+                        || (excludeDying && mRemovingUserIds.get(ui.id))
                         || (excludePreCreated && ui.preCreated)) {
                     continue;
                 }
@@ -1381,17 +1370,6 @@
         }
     }
 
-    @GuardedBy("mUsersLock")
-    private boolean isDyingLU(UserInfo ui) {
-        if (mRemovingUserIds.get(ui.id)) {
-            return true;
-        }
-        if (ui.isEphemeral() && ui.isInitialized() && ui.id != getCurrentUserId()) {
-            return true;
-        }
-        return false;
-    }
-
     @Override
     public List<UserInfo> getProfiles(@UserIdInt int userId, boolean enabledOnly) {
         boolean returnFullInfo;
@@ -1946,16 +1924,20 @@
     private void showConfirmCredentialToDisableQuietMode(
             @UserIdInt int userId, @Nullable IntentSender target, @Nullable String callingPackage) {
         if (android.app.admin.flags.Flags.quietModeCredentialBugFix()) {
-            // TODO (b/308121702) It may be brittle to rely on user states to check profile state
-            int state;
-            synchronized (mUserStates) {
-                state = mUserStates.get(userId, UserState.STATE_NONE);
-            }
-            if (state != UserState.STATE_NONE) {
-                Slog.i(LOG_TAG,
-                        "showConfirmCredentialToDisableQuietMode() called too early, user " + userId
-                                + " is still alive.");
-                return;
+            if (!android.multiuser.Flags.restrictQuietModeCredentialBugFixToManagedProfiles()
+                    || getUserInfo(userId).isManagedProfile()) {
+                // TODO (b/308121702) It may be brittle to rely on user states to check managed
+                //  profile state
+                int state;
+                synchronized (mUserStates) {
+                    state = mUserStates.get(userId, UserState.STATE_NONE);
+                }
+                if (state != UserState.STATE_NONE) {
+                    Slog.i(LOG_TAG,
+                            "showConfirmCredentialToDisableQuietMode() called too early, managed "
+                                    + "user " + userId + " is still alive.");
+                    return;
+                }
             }
         }
         // otherwise, we show a profile challenge to trigger decryption of the user
@@ -4234,6 +4216,13 @@
                                             || mNextSerialNumber <= userData.info.id) {
                                         mNextSerialNumber = userData.info.id + 1;
                                     }
+                                    if (userData.info.isEphemeral() && !userData.info.preCreated
+                                            && userData.info.id != UserHandle.USER_SYSTEM) {
+                                        // Mark ephemeral user as slated for deletion.
+                                        addRemovingUserIdLocked(userData.info.id);
+                                        userData.info.partial = true;
+                                        userData.info.flags |= UserInfo.FLAG_DISABLED;
+                                    }
                                 }
                             }
                         } else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
index 5aad570..884c26c 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
@@ -19,12 +19,9 @@
 import android.annotation.NonNull;
 import android.os.BatteryConsumer;
 
-import com.android.internal.os.PowerStats;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -206,25 +203,9 @@
         return mPowerComponents;
     }
 
-    private static final PowerStatsProcessor NO_OP_PROCESSOR =
-            new PowerStatsProcessor() {
-                @Override
-                void finish(PowerComponentAggregatedPowerStats stats) {
-                }
-
-                @Override
-                String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
-                    return Arrays.toString(stats);
-                }
-
-                @Override
-                String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
-                    return descriptor.getStateLabel(key) + " " + Arrays.toString(stats);
-                }
-
-                @Override
-                String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
-                    return Arrays.toString(stats);
-                }
-            };
+    private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() {
+        @Override
+        void finish(PowerComponentAggregatedPowerStats stats) {
+        }
+    };
 }
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 49c4000..9a41551 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -463,11 +463,17 @@
     public static class BatteryStatsConfig {
         static final int RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG = 1 << 0;
         static final int RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG = 1 << 1;
-        static final long DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD =
-                TimeUnit.HOURS.toMillis(1);
 
         private final int mFlags;
-        private SparseLongArray mPowerStatsThrottlePeriods;
+        private final Long mDefaultPowerStatsThrottlePeriod;
+        private final Map<String, Long> mPowerStatsThrottlePeriods;
+
+        @VisibleForTesting
+        public BatteryStatsConfig() {
+            mFlags = 0;
+            mDefaultPowerStatsThrottlePeriod = 0L;
+            mPowerStatsThrottlePeriods = Map.of();
+        }
 
         private BatteryStatsConfig(Builder builder) {
             int flags = 0;
@@ -478,6 +484,7 @@
                 flags |= RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
             }
             mFlags = flags;
+            mDefaultPowerStatsThrottlePeriod = builder.mDefaultPowerStatsThrottlePeriod;
             mPowerStatsThrottlePeriods = builder.mPowerStatsThrottlePeriods;
         }
 
@@ -485,7 +492,7 @@
          * Returns whether a BatteryStats reset should occur on unplug when the battery level is
          * high.
          */
-        boolean shouldResetOnUnplugHighBatteryLevel() {
+        public boolean shouldResetOnUnplugHighBatteryLevel() {
             return (mFlags & RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG)
                     == RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG;
         }
@@ -494,14 +501,18 @@
          * Returns whether a BatteryStats reset should occur on unplug if the battery charge a
          * significant amount since it has been plugged in.
          */
-        boolean shouldResetOnUnplugAfterSignificantCharge() {
+        public boolean shouldResetOnUnplugAfterSignificantCharge() {
             return (mFlags & RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG)
                     == RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
         }
 
-        long getPowerStatsThrottlePeriod(@BatteryConsumer.PowerComponent int powerComponent) {
-            return mPowerStatsThrottlePeriods.get(powerComponent,
-                    DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD);
+        /**
+         * Returns  the minimum amount of time (in millis) to wait between passes
+         * of power stats collection for the specified power component.
+         */
+        public long getPowerStatsThrottlePeriod(String powerComponentName) {
+            return mPowerStatsThrottlePeriods.getOrDefault(powerComponentName,
+                    mDefaultPowerStatsThrottlePeriod);
         }
 
         /**
@@ -510,18 +521,19 @@
         public static class Builder {
             private boolean mResetOnUnplugHighBatteryLevel;
             private boolean mResetOnUnplugAfterSignificantCharge;
-            private SparseLongArray mPowerStatsThrottlePeriods;
+            public static final long DEFAULT_POWER_STATS_THROTTLE_PERIOD =
+                    TimeUnit.HOURS.toMillis(1);
+            public static final long DEFAULT_POWER_STATS_THROTTLE_PERIOD_CPU =
+                    TimeUnit.MINUTES.toMillis(1);
+            private long mDefaultPowerStatsThrottlePeriod = DEFAULT_POWER_STATS_THROTTLE_PERIOD;
+            private final Map<String, Long> mPowerStatsThrottlePeriods = new HashMap<>();
 
             public Builder() {
                 mResetOnUnplugHighBatteryLevel = true;
                 mResetOnUnplugAfterSignificantCharge = true;
-                mPowerStatsThrottlePeriods = new SparseLongArray();
-                setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU,
-                        TimeUnit.MINUTES.toMillis(1));
-                setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
-                        TimeUnit.HOURS.toMillis(1));
-                setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_WIFI,
-                        TimeUnit.HOURS.toMillis(1));
+                setPowerStatsThrottlePeriodMillis(BatteryConsumer.powerComponentIdToString(
+                                BatteryConsumer.POWER_COMPONENT_CPU),
+                        DEFAULT_POWER_STATS_THROTTLE_PERIOD_CPU);
             }
 
             /**
@@ -553,9 +565,18 @@
              * Sets the minimum amount of time (in millis) to wait between passes
              * of power stats collection for the specified power component.
              */
-            public Builder setPowerStatsThrottlePeriodMillis(
-                    @BatteryConsumer.PowerComponent int powerComponent, long periodMs) {
-                mPowerStatsThrottlePeriods.put(powerComponent, periodMs);
+            public Builder setPowerStatsThrottlePeriodMillis(String powerComponentName,
+                    long periodMs) {
+                mPowerStatsThrottlePeriods.put(powerComponentName, periodMs);
+                return this;
+            }
+
+            /**
+             * Sets the minimum amount of time (in millis) to wait between passes
+             * of power stats collection for any components not configured explicitly.
+             */
+            public Builder setDefaultPowerStatsThrottlePeriodMillis(long periodMs) {
+                mDefaultPowerStatsThrottlePeriod = periodMs;
                 return this;
             }
         }
@@ -1586,8 +1607,7 @@
     protected final Constants mConstants;
 
     @VisibleForTesting
-    @GuardedBy("this")
-    protected BatteryStatsConfig mBatteryStatsConfig;
+    protected final BatteryStatsConfig mBatteryStatsConfig;
 
     @GuardedBy("this")
     private AlarmManager mAlarmManager = null;
@@ -1933,6 +1953,11 @@
         }
 
         @Override
+        public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+            return mBatteryStatsConfig.getPowerStatsThrottlePeriod(powerComponentName);
+        }
+
+        @Override
         public PowerStatsUidResolver getUidResolver() {
             return mPowerStatsUidResolver;
         }
@@ -11167,19 +11192,14 @@
                 mConstants.MAX_HISTORY_FILES, mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator,
                 mClock, mMonotonicClock, traceDelegate, eventLogger);
 
-        mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector,
-                mBatteryStatsConfig.getPowerStatsThrottlePeriod(
-                        BatteryConsumer.POWER_COMPONENT_CPU));
+        mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector);
         mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
 
         mMobileRadioPowerStatsCollector = new MobileRadioPowerStatsCollector(
-                mPowerStatsCollectorInjector, mBatteryStatsConfig.getPowerStatsThrottlePeriod(
-                BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO));
+                mPowerStatsCollectorInjector);
         mMobileRadioPowerStatsCollector.addConsumer(this::recordPowerStats);
 
-        mWifiPowerStatsCollector = new WifiPowerStatsCollector(
-                mPowerStatsCollectorInjector, mBatteryStatsConfig.getPowerStatsThrottlePeriod(
-                BatteryConsumer.POWER_COMPONENT_WIFI));
+        mWifiPowerStatsCollector = new WifiPowerStatsCollector(mPowerStatsCollectorInjector);
         mWifiPowerStatsCollector.addConsumer(this::recordPowerStats);
 
         mStartCount++;
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index f53a1b0..b5ef67b 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -59,6 +59,7 @@
         KernelCpuStatsReader getKernelCpuStatsReader();
         ConsumedEnergyRetriever getConsumedEnergyRetriever();
         IntSupplier getVoltageSupplier();
+        long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
 
         default int getDefaultCpuPowerBrackets() {
             return DEFAULT_CPU_POWER_BRACKETS;
@@ -94,9 +95,11 @@
     private int mLastVoltageMv;
     private long[] mLastConsumedEnergyUws;
 
-    public CpuPowerStatsCollector(Injector injector, long throttlePeriodMs) {
-        super(injector.getHandler(), throttlePeriodMs, injector.getUidResolver(),
-                injector.getClock());
+    CpuPowerStatsCollector(Injector injector) {
+        super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
+                        BatteryConsumer.powerComponentIdToString(
+                                BatteryConsumer.POWER_COMPONENT_CPU)),
+                injector.getUidResolver(), injector.getClock());
         mInjector = injector;
     }
 
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
index 1bcb2c4..2a02bd0 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
@@ -44,7 +44,7 @@
      * Declare that the stats array has a section capturing CPU time per scaling step
      */
     public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
-        mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount);
+        mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount, "steps");
         mDeviceCpuTimeByScalingStepCount = scalingStepCount;
     }
 
@@ -72,7 +72,7 @@
      * Declare that the stats array has a section capturing CPU time in each cluster
      */
     public void addDeviceSectionCpuTimeByCluster(int clusterCount) {
-        mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount);
+        mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount, "clusters");
         mDeviceCpuTimeByClusterCount = clusterCount;
     }
 
@@ -102,7 +102,7 @@
     public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
         mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
         updatePowerBracketCount();
-        mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount);
+        mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount, "time");
     }
 
     private void updatePowerBracketCount() {
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
index c34b8a8..57b7259 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
@@ -16,7 +16,6 @@
 
 package com.android.server.power.stats;
 
-import android.os.BatteryStats;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -487,64 +486,4 @@
             stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
         }
     }
-
-    @Override
-    public String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
-        unpackPowerStatsDescriptor(descriptor);
-        StringBuilder sb = new StringBuilder();
-        int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
-        sb.append("steps: [");
-        for (int step = 0; step < cpuScalingStepCount; step++) {
-            if (step != 0) {
-                sb.append(", ");
-            }
-            sb.append(mStatsLayout.getTimeByScalingStep(stats, step));
-        }
-        int clusterCount = mStatsLayout.getCpuClusterCount();
-        sb.append("] clusters: [");
-        for (int cluster = 0; cluster < clusterCount; cluster++) {
-            if (cluster != 0) {
-                sb.append(", ");
-            }
-            sb.append(mStatsLayout.getTimeByCluster(stats, cluster));
-        }
-        sb.append("] uptime: ").append(mStatsLayout.getUsageDuration(stats));
-        int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
-        if (energyConsumerCount > 0) {
-            sb.append(" energy: [");
-            for (int i = 0; i < energyConsumerCount; i++) {
-                if (i != 0) {
-                    sb.append(", ");
-                }
-                sb.append(mStatsLayout.getConsumedEnergy(stats, i));
-            }
-            sb.append("]");
-        }
-        sb.append(" power: ").append(
-                BatteryStats.formatCharge(mStatsLayout.getDevicePowerEstimate(stats)));
-        return sb.toString();
-    }
-
-    @Override
-    String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
-        // Unsupported for this power component
-        return null;
-    }
-
-    @Override
-    public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
-        unpackPowerStatsDescriptor(descriptor);
-        StringBuilder sb = new StringBuilder();
-        sb.append("[");
-        int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
-        for (int bracket = 0; bracket < powerBracketCount; bracket++) {
-            if (bracket != 0) {
-                sb.append(", ");
-            }
-            sb.append(mStatsLayout.getUidTimeByPowerBracket(stats, bracket));
-        }
-        sb.append("] power: ").append(
-                BatteryStats.formatCharge(mStatsLayout.getUidPowerEstimate(stats)));
-        return sb.toString();
-    }
 }
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
index 7bc6817..a96e01b 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
@@ -73,6 +73,7 @@
         Handler getHandler();
         Clock getClock();
         PowerStatsUidResolver getUidResolver();
+        long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
         PackageManager getPackageManager();
         ConsumedEnergyRetriever getConsumedEnergyRetriever();
         IntSupplier getVoltageSupplier();
@@ -104,8 +105,11 @@
     private long mLastCallDuration;
     private long mLastScanDuration;
 
-    public MobileRadioPowerStatsCollector(Injector injector, long throttlePeriodMs) {
-        super(injector.getHandler(), throttlePeriodMs, injector.getUidResolver(),
+    MobileRadioPowerStatsCollector(Injector injector) {
+        super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
+                        BatteryConsumer.powerComponentIdToString(
+                                BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)),
+                injector.getUidResolver(),
                 injector.getClock());
         mInjector = injector;
     }
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
index 81d7c2f..07d78f8 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
@@ -64,29 +64,30 @@
     }
 
     void addDeviceMobileActivity() {
-        mDeviceSleepTimePosition = addDeviceSection(1);
-        mDeviceIdleTimePosition = addDeviceSection(1);
-        mDeviceScanTimePosition = addDeviceSection(1);
-        mDeviceCallTimePosition = addDeviceSection(1);
+        mDeviceSleepTimePosition = addDeviceSection(1, "sleep");
+        mDeviceIdleTimePosition = addDeviceSection(1, "idle");
+        mDeviceScanTimePosition = addDeviceSection(1, "scan");
+        mDeviceCallTimePosition = addDeviceSection(1, "call", FLAG_OPTIONAL);
     }
 
     void addStateStats() {
-        mStateRxTimePosition = addStateSection(1);
+        mStateRxTimePosition = addStateSection(1, "rx");
         mStateTxTimesCount = ModemActivityInfo.getNumTxPowerLevels();
-        mStateTxTimesPosition = addStateSection(mStateTxTimesCount);
+        mStateTxTimesPosition = addStateSection(mStateTxTimesCount, "tx");
     }
 
     void addUidNetworkStats() {
-        mUidRxBytesPosition = addUidSection(1);
-        mUidTxBytesPosition = addUidSection(1);
-        mUidRxPacketsPosition = addUidSection(1);
-        mUidTxPacketsPosition = addUidSection(1);
+        mUidRxPacketsPosition = addUidSection(1, "rx-pkts");
+        mUidRxBytesPosition = addUidSection(1, "rx-B");
+        mUidTxPacketsPosition = addUidSection(1, "tx-pkts");
+        mUidTxBytesPosition = addUidSection(1, "tx-B");
     }
 
     @Override
     public void addDeviceSectionPowerEstimate() {
         super.addDeviceSectionPowerEstimate();
-        mDeviceCallPowerPosition = addDeviceSection(1);
+        // Printed as part of the PhoneCallPowerStatsProcessor
+        mDeviceCallPowerPosition = addDeviceSection(1, "call-power", FLAG_HIDDEN);
     }
 
     public void setDeviceSleepTime(long[] stats, long durationMillis) {
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
index c97c64b..eebed2f 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
@@ -398,37 +398,4 @@
             }
         }
     }
-
-    @Override
-    String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
-        unpackPowerStatsDescriptor(descriptor);
-        return "idle: " + mStatsLayout.getDeviceIdleTime(stats)
-                + " sleep: " + mStatsLayout.getDeviceSleepTime(stats)
-                + " scan: " + mStatsLayout.getDeviceScanTime(stats)
-                + " power: " + mStatsLayout.getDevicePowerEstimate(stats);
-    }
-
-    @Override
-    String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
-        unpackPowerStatsDescriptor(descriptor);
-        StringBuilder sb = new StringBuilder();
-        sb.append(descriptor.getStateLabel(key));
-        sb.append(" rx: ").append(mStatsLayout.getStateRxTime(stats));
-        sb.append(" tx: ");
-        for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
-            if (txLevel != 0) {
-                sb.append(", ");
-            }
-            sb.append(mStatsLayout.getStateTxTime(stats, txLevel));
-        }
-        return sb.toString();
-    }
-
-    @Override
-    String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
-        unpackPowerStatsDescriptor(descriptor);
-        return "rx: " + mStatsLayout.getUidRxPackets(stats)
-                + " tx: " + mStatsLayout.getUidTxPackets(stats)
-                + " power: " + mStatsLayout.getUidPowerEstimate(stats);
-    }
 }
diff --git a/services/core/java/com/android/server/power/stats/MultiStateStats.java b/services/core/java/com/android/server/power/stats/MultiStateStats.java
index 6c4a2b6..a822281 100644
--- a/services/core/java/com/android/server/power/stats/MultiStateStats.java
+++ b/services/core/java/com/android/server/power/stats/MultiStateStats.java
@@ -28,10 +28,8 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
-import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.function.Consumer;
-import java.util.function.Function;
 
 /**
  * Maintains multidimensional multi-state stats.  States could be something like on-battery (0,1),
@@ -287,6 +285,14 @@
         mCounter = new LongArrayMultiStateCounter(factory.mSerialStateCount, dimensionCount);
     }
 
+    public int getDimensionCount() {
+        return mFactory.mDimensionCount;
+    }
+
+    public States[] getStates() {
+        return mFactory.mStates;
+    }
+
     /**
      * Copies time-in-state and timestamps from the supplied prototype. Does not
      * copy accumulated counts.
@@ -343,11 +349,6 @@
         mTracking = false;
     }
 
-    @Override
-    public String toString() {
-        return mCounter.toString();
-    }
-
     /**
      * Stores contents in an XML doc.
      */
@@ -451,10 +452,9 @@
         return true;
     }
 
-    /**
-     * Prints the accumulated stats, one line of every combination of states that has data.
-     */
-    public void dump(PrintWriter pw, Function<long[], String> statsFormatter) {
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
         long[] values = new long[mCounter.getArrayLength()];
         States.forEachTrackedStateCombination(mFactory.mStates, states -> {
             mCounter.getCounts(values, mFactory.getSerialState(states));
@@ -469,18 +469,24 @@
                 return;
             }
 
-            StringBuilder sb = new StringBuilder();
+            if (!sb.isEmpty()) {
+                sb.append("\n");
+            }
+
+            sb.append("(");
+            boolean first = true;
             for (int i = 0; i < states.length; i++) {
                 if (mFactory.mStates[i].mTracked) {
-                    if (sb.length() != 0) {
+                    if (!first) {
                         sb.append(" ");
                     }
+                    first = false;
                     sb.append(mFactory.mStates[i].mLabels[states[i]]);
                 }
             }
-            sb.append(" ");
-            sb.append(statsFormatter.apply(values));
-            pw.println(sb);
+            sb.append(") ");
+            sb.append(Arrays.toString(values));
         });
+        return sb.toString();
     }
 }
diff --git a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
index 62b653f..5c545fd 100644
--- a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
@@ -76,21 +76,4 @@
                     stats.setDeviceStats(states, mTmpDeviceStats);
                 });
     }
-
-    @Override
-    String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
-        return "power: " + mStatsLayout.getDevicePowerEstimate(stats);
-    }
-
-    @Override
-    String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
-        // Unsupported for this power component
-        return null;
-    }
-
-    @Override
-    String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
-        // Unsupported for this power component
-        return null;
-    }
 }
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
index 6d58307..0528733 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -436,36 +436,76 @@
 
     void dumpDevice(IndentingPrintWriter ipw) {
         if (mDeviceStats != null) {
-            ipw.println(mPowerStatsDescriptor.name);
-            ipw.increaseIndent();
-            mDeviceStats.dump(ipw, stats ->
-                    mConfig.getProcessor().deviceStatsToString(mPowerStatsDescriptor, stats));
-            ipw.decreaseIndent();
+            dumpMultiStateStats(ipw, mDeviceStats, mPowerStatsDescriptor.name, null,
+                    mPowerStatsDescriptor.getDeviceStatsFormatter());
         }
 
         if (mStateStats.size() != 0) {
             ipw.increaseIndent();
-            ipw.println(mPowerStatsDescriptor.name + " states");
-            ipw.increaseIndent();
+            String header = mPowerStatsDescriptor.name + " states";
+            PowerStats.PowerStatsFormatter formatter =
+                    mPowerStatsDescriptor.getStateStatsFormatter();
             for (int i = 0; i < mStateStats.size(); i++) {
                 int key = mStateStats.keyAt(i);
+                String stateLabel = mPowerStatsDescriptor.getStateLabel(key);
                 MultiStateStats stateStats = mStateStats.valueAt(i);
-                stateStats.dump(ipw, stats ->
-                        mConfig.getProcessor().stateStatsToString(mPowerStatsDescriptor, key,
-                                stats));
+                dumpMultiStateStats(ipw, stateStats, header, stateLabel, formatter);
             }
             ipw.decreaseIndent();
-            ipw.decreaseIndent();
         }
     }
 
     void dumpUid(IndentingPrintWriter ipw, int uid) {
         UidStats uidStats = mUidStats.get(uid);
         if (uidStats != null && uidStats.stats != null) {
-            ipw.println(mPowerStatsDescriptor.name);
-            ipw.increaseIndent();
-            uidStats.stats.dump(ipw, stats ->
-                    mConfig.getProcessor().uidStatsToString(mPowerStatsDescriptor, stats));
+            dumpMultiStateStats(ipw, uidStats.stats, mPowerStatsDescriptor.name, null,
+                    mPowerStatsDescriptor.getUidStatsFormatter());
+        }
+    }
+
+    private void dumpMultiStateStats(IndentingPrintWriter ipw, MultiStateStats stats,
+            String header, String additionalLabel,
+            PowerStats.PowerStatsFormatter statsFormatter) {
+        boolean[] firstLine = new boolean[]{true};
+        long[] values = new long[stats.getDimensionCount()];
+        MultiStateStats.States[] stateInfo = stats.getStates();
+        MultiStateStats.States.forEachTrackedStateCombination(stateInfo, states -> {
+            stats.getStats(values, states);
+            boolean nonZero = false;
+            for (long value : values) {
+                if (value != 0) {
+                    nonZero = true;
+                    break;
+                }
+            }
+            if (!nonZero) {
+                return;
+            }
+
+            if (firstLine[0]) {
+                ipw.println(header);
+                ipw.increaseIndent();
+            }
+            firstLine[0] = false;
+            StringBuilder sb = new StringBuilder();
+            sb.append("(");
+            boolean first = true;
+            for (int i = 0; i < states.length; i++) {
+                if (stateInfo[i].isTracked()) {
+                    if (!first) {
+                        sb.append(" ");
+                    }
+                    first = false;
+                    sb.append(stateInfo[i].getLabels()[states[i]]);
+                }
+            }
+            if (additionalLabel != null) {
+                sb.append(" ").append(additionalLabel);
+            }
+            sb.append(") ").append(statsFormatter.format(values));
+            ipw.println(sb);
+        });
+        if (!firstLine[0]) {
             ipw.decreaseIndent();
         }
     }
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
index aa96409..58efd94 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
@@ -33,13 +33,20 @@
     private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
     private static final String EXTRA_UID_POWER_POSITION = "up";
 
-    protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
     protected static final int UNSUPPORTED = -1;
+    protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
+    protected static final int FLAG_OPTIONAL = 1;
+    protected static final int FLAG_HIDDEN = 2;
+    protected static final int FLAG_FORMAT_AS_POWER = 4;
 
     private int mDeviceStatsArrayLength;
     private int mStateStatsArrayLength;
     private int mUidStatsArrayLength;
 
+    private StringBuilder mDeviceFormat = new StringBuilder();
+    private StringBuilder mStateFormat = new StringBuilder();
+    private StringBuilder mUidFormat = new StringBuilder();
+
     protected int mDeviceDurationPosition = UNSUPPORTED;
     private int mDeviceEnergyConsumerPosition;
     private int mDeviceEnergyConsumerCount;
@@ -65,29 +72,71 @@
         return mUidStatsArrayLength;
     }
 
-    protected int addDeviceSection(int length) {
+    /**
+     * @param label should not contain either spaces or colons
+     */
+    private void appendFormat(StringBuilder sb, int position, int length, String label,
+            int flags) {
+        if ((flags & FLAG_HIDDEN) != 0) {
+            return;
+        }
+
+        if (!sb.isEmpty()) {
+            sb.append(' ');
+        }
+
+        sb.append(label).append(':');
+        sb.append(position);
+        if (length != 1) {
+            sb.append('[').append(length).append(']');
+        }
+        if ((flags & FLAG_FORMAT_AS_POWER) != 0) {
+            sb.append('p');
+        }
+        if ((flags & FLAG_OPTIONAL) != 0) {
+            sb.append('?');
+        }
+    }
+
+    protected int addDeviceSection(int length, String label, int flags) {
         int position = mDeviceStatsArrayLength;
         mDeviceStatsArrayLength += length;
+        appendFormat(mDeviceFormat, position, length, label, flags);
         return position;
     }
 
-    protected int addStateSection(int length) {
+    protected int addDeviceSection(int length, String label) {
+        return addDeviceSection(length, label, 0);
+    }
+
+    protected int addStateSection(int length, String label, int flags) {
         int position = mStateStatsArrayLength;
         mStateStatsArrayLength += length;
+        appendFormat(mStateFormat, position, length, label, flags);
         return position;
     }
 
-    protected int addUidSection(int length) {
+    protected int addStateSection(int length, String label) {
+        return addStateSection(length, label, 0);
+    }
+
+
+    protected int addUidSection(int length, String label, int flags) {
         int position = mUidStatsArrayLength;
         mUidStatsArrayLength += length;
+        appendFormat(mUidFormat, position, length, label, flags);
         return position;
     }
 
+    protected int addUidSection(int length, String label) {
+        return addUidSection(length, label, 0);
+    }
+
     /**
      * Declare that the stats array has a section capturing usage duration
      */
     public void addDeviceSectionUsageDuration() {
-        mDeviceDurationPosition = addDeviceSection(1);
+        mDeviceDurationPosition = addDeviceSection(1, "usage", FLAG_OPTIONAL);
     }
 
     /**
@@ -109,7 +158,7 @@
      * PowerStatsService.
      */
     public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
-        mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount);
+        mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount, "energy");
         mDeviceEnergyConsumerCount = energyConsumerCount;
     }
 
@@ -137,7 +186,8 @@
      * Declare that the stats array has a section capturing a power estimate
      */
     public void addDeviceSectionPowerEstimate() {
-        mDevicePowerEstimatePosition = addDeviceSection(1);
+        mDevicePowerEstimatePosition = addDeviceSection(1, "power",
+                FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
     }
 
     /**
@@ -159,7 +209,7 @@
      * Declare that the UID stats array has a section capturing a power estimate
      */
     public void addUidSectionPowerEstimate() {
-        mUidPowerEstimatePosition = addUidSection(1);
+        mUidPowerEstimatePosition = addUidSection(1, "power", FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
     }
 
     /**
@@ -195,6 +245,9 @@
                 mDeviceEnergyConsumerCount);
         extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
         extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
+        extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, mDeviceFormat.toString());
+        extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, mStateFormat.toString());
+        extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, mUidFormat.toString());
     }
 
     /**
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
index 0d5c542..2fd0b9a 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
@@ -19,8 +19,6 @@
 import android.annotation.Nullable;
 import android.util.Log;
 
-import com.android.internal.os.PowerStats;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -47,12 +45,6 @@
 
     abstract void finish(PowerComponentAggregatedPowerStats stats);
 
-    abstract String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats);
-
-    abstract String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats);
-
-    abstract String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats);
-
     protected static class PowerEstimationPlan {
         private final AggregatedPowerStatsConfig.PowerComponent mConfig;
         public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>();
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
index 6321053..bd04199 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
@@ -56,6 +56,7 @@
         Handler getHandler();
         Clock getClock();
         PowerStatsUidResolver getUidResolver();
+        long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
         PackageManager getPackageManager();
         ConsumedEnergyRetriever getConsumedEnergyRetriever();
         IntSupplier getVoltageSupplier();
@@ -92,9 +93,11 @@
     private final SparseArray<WifiScanTimes> mLastScanTimes = new SparseArray<>();
     private long mLastWifiActiveDuration;
 
-    public WifiPowerStatsCollector(Injector injector, long throttlePeriodMs) {
-        super(injector.getHandler(), throttlePeriodMs, injector.getUidResolver(),
-                injector.getClock());
+    WifiPowerStatsCollector(Injector injector) {
+        super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
+                        BatteryConsumer.powerComponentIdToString(
+                                BatteryConsumer.POWER_COMPONENT_WIFI)),
+                injector.getUidResolver(), injector.getClock());
         mInjector = injector;
     }
 
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java
index 0fa6ec6..e2e8226 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java
@@ -65,28 +65,28 @@
         mPowerReportingSupported = powerReportingSupported;
         if (mPowerReportingSupported) {
             mDeviceActiveTimePosition = UNSPECIFIED;
-            mDeviceRxTimePosition = addDeviceSection(1);
-            mDeviceTxTimePosition = addDeviceSection(1);
-            mDeviceIdleTimePosition = addDeviceSection(1);
-            mDeviceScanTimePosition = addDeviceSection(1);
+            mDeviceRxTimePosition = addDeviceSection(1, "rx");
+            mDeviceTxTimePosition = addDeviceSection(1, "tx");
+            mDeviceIdleTimePosition = addDeviceSection(1, "idle");
+            mDeviceScanTimePosition = addDeviceSection(1, "scan");
         } else {
-            mDeviceActiveTimePosition = addDeviceSection(1);
+            mDeviceActiveTimePosition = addDeviceSection(1, "rx-tx");
             mDeviceRxTimePosition = UNSPECIFIED;
             mDeviceTxTimePosition = UNSPECIFIED;
             mDeviceIdleTimePosition = UNSPECIFIED;
             mDeviceScanTimePosition = UNSPECIFIED;
         }
-        mDeviceBasicScanTimePosition = addDeviceSection(1);
-        mDeviceBatchedScanTimePosition = addDeviceSection(1);
+        mDeviceBasicScanTimePosition = addDeviceSection(1, "basic-scan", FLAG_OPTIONAL);
+        mDeviceBatchedScanTimePosition = addDeviceSection(1, "batched-scan", FLAG_OPTIONAL);
     }
 
     void addUidNetworkStats() {
-        mUidRxBytesPosition = addUidSection(1);
-        mUidTxBytesPosition = addUidSection(1);
-        mUidRxPacketsPosition = addUidSection(1);
-        mUidTxPacketsPosition = addUidSection(1);
-        mUidScanTimePosition = addUidSection(1);
-        mUidBatchScanTimePosition = addUidSection(1);
+        mUidRxPacketsPosition = addUidSection(1, "rx-pkts");
+        mUidRxBytesPosition = addUidSection(1, "rx-B");
+        mUidTxPacketsPosition = addUidSection(1, "tx-pkts");
+        mUidTxBytesPosition = addUidSection(1, "tx-B");
+        mUidScanTimePosition = addUidSection(1, "scan", FLAG_OPTIONAL);
+        mUidBatchScanTimePosition = addUidSection(1, "batched-scan", FLAG_OPTIONAL);
     }
 
     public boolean isPowerReportingSupported() {
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
index 5e9cc40..a4a2e18 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
@@ -389,37 +389,4 @@
             }
         }
     }
-
-    @Override
-    String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
-        unpackPowerStatsDescriptor(descriptor);
-        if (mHasWifiPowerController) {
-            return "rx: " + mStatsLayout.getDeviceRxTime(stats)
-                    + " tx: " + mStatsLayout.getDeviceTxTime(stats)
-                    + " scan: " + mStatsLayout.getDeviceScanTime(stats)
-                    + " idle: " + mStatsLayout.getDeviceIdleTime(stats)
-                    + " power: " + mStatsLayout.getDevicePowerEstimate(stats);
-        } else {
-            return "active: " + mStatsLayout.getDeviceActiveTime(stats)
-                    + " scan: " + mStatsLayout.getDeviceBasicScanTime(stats)
-                    + " batched-scan: " + mStatsLayout.getDeviceBatchedScanTime(stats)
-                    + " power: " + mStatsLayout.getDevicePowerEstimate(stats);
-        }
-    }
-
-    @Override
-    String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
-        // Unsupported for this power component
-        return null;
-    }
-
-    @Override
-    String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
-        unpackPowerStatsDescriptor(descriptor);
-        return "rx: " + mStatsLayout.getUidRxPackets(stats)
-                + " tx: " + mStatsLayout.getUidTxPackets(stats)
-                + " scan: " + mStatsLayout.getUidScanTime(stats)
-                + " batched-scan: " + mStatsLayout.getUidBatchedScanTime(stats)
-                + " power: " + mStatsLayout.getUidPowerEstimate(stats);
-    }
 }
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index e3e478d..c1b825b 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -49,10 +49,13 @@
 import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;
 
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__GESTURE_SHORTCUT_TYPE__TRIPLE_TAP;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__HARDWARE_SHORTCUT_TYPE__VOLUME_KEY;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__QS_SHORTCUT_TYPE__QUICK_SETTINGS;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_BUTTON;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_GESTURE;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__UNKNOWN_TYPE;
 import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC;
 import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC;
 import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__GEO;
@@ -61,7 +64,6 @@
 import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__UNKNOWN;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 import static com.android.server.stats.Flags.addMobileBytesTransferByProcStatePuller;
-import static com.android.server.stats.Flags.statsPullNetworkStatsManagerInitOrderFix;
 import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
 import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
 import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines;
@@ -431,12 +433,6 @@
     public static final boolean ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER =
                 addMobileBytesTransferByProcStatePuller();
 
-    /**
-     * Whether or not to enable the mNetworkStatsManager initialization order fix
-     */
-    private static final boolean ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX =
-                statsPullNetworkStatsManagerInitOrderFix();
-
     // Puller locks
     private final Object mDataBytesTransferLock = new Object();
     private final Object mBluetoothBytesTransferLock = new Object();
@@ -799,7 +795,7 @@
                     case FrameworkStatsLog.KEYSTORE2_CRASH_STATS:
                         return pullKeystoreAtoms(atomTag, data);
                     case FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS:
-                        return pullAccessibilityShortcutStatsLocked(atomTag, data);
+                        return pullAccessibilityShortcutStatsLocked(data);
                     case FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS:
                         return pullAccessibilityFloatingMenuStatsLocked(atomTag, data);
                     case FrameworkStatsLog.MEDIA_CAPABILITIES:
@@ -840,9 +836,7 @@
                 registerEventListeners();
             });
         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
-            if (ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) {
-                initNetworkStatsManager();
-            }
+            initNetworkStatsManager();
             BackgroundThread.getHandler().post(() -> {
                 // Network stats related pullers can only be initialized after service is ready.
                 initAndRegisterNetworkStatsPullers();
@@ -863,9 +857,6 @@
                 mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
         mStatsSubscriptionsListener = new StatsSubscriptionsListener(mSubscriptionManager);
         mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class);
-        if (!ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) {
-            initNetworkStatsManager();
-        }
 
         // Initialize DiskIO
         mStoragedUidIoStatsReader = new StoragedUidIoStatsReader();
@@ -1047,10 +1038,8 @@
      */
     @NonNull
     private NetworkStatsManager getNetworkStatsManager() {
-        if (ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) {
-            if (mNetworkStatsManager == null) {
-                throw new IllegalStateException("NetworkStatsManager is not ready");
-            }
+        if (mNetworkStatsManager == null) {
+            throw new IllegalStateException("NetworkStatsManager is not ready");
         }
         return mNetworkStatsManager;
     }
@@ -4774,7 +4763,10 @@
         }
     }
 
-    int pullAccessibilityShortcutStatsLocked(int atomTag, List<StatsEvent> pulledData) {
+    /**
+     * Pulls ACCESSIBILITY_SHORTCUT_STATS atom
+     */
+    int pullAccessibilityShortcutStatsLocked(List<StatsEvent> pulledData) {
         UserManager userManager = mContext.getSystemService(UserManager.class);
         if (userManager == null) {
             return StatsManager.PULL_SKIP;
@@ -4782,10 +4774,6 @@
         final long token = Binder.clearCallingIdentity();
         try {
             final ContentResolver resolver = mContext.getContentResolver();
-            final int hardware_shortcut_type =
-                    FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
-            final int triple_tap_shortcut =
-                    FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
             for (UserInfo userInfo : userManager.getUsers()) {
                 final int userId = userInfo.getUserHandle().getIdentifier();
 
@@ -4803,15 +4791,22 @@
                     final int hardware_shortcut_service_num = countAccessibilityServices(
                             hardware_shortcut_list);
 
+                    final String qs_shortcut_list = Settings.Secure.getStringForUser(resolver,
+                            Settings.Secure.ACCESSIBILITY_QS_TARGETS, userId);
+                    final boolean qs_shortcut_enabled = !TextUtils.isEmpty(qs_shortcut_list);
+
                     // only allow magnification to use it for now
                     final int triple_tap_service_num = Settings.Secure.getIntForUser(resolver,
                             Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId);
-
-                    pulledData.add(
-                            FrameworkStatsLog.buildStatsEvent(atomTag,
-                                    software_shortcut_type, software_shortcut_service_num,
-                                    hardware_shortcut_type, hardware_shortcut_service_num,
-                                    triple_tap_shortcut, triple_tap_service_num));
+                    pulledData.add(FrameworkStatsLog.buildStatsEvent(
+                            FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS,
+                            software_shortcut_type, software_shortcut_service_num,
+                            ACCESSIBILITY_SHORTCUT_STATS__HARDWARE_SHORTCUT_TYPE__VOLUME_KEY,
+                            hardware_shortcut_service_num,
+                            ACCESSIBILITY_SHORTCUT_STATS__GESTURE_SHORTCUT_TYPE__TRIPLE_TAP,
+                            triple_tap_service_num,
+                            ACCESSIBILITY_SHORTCUT_STATS__QS_SHORTCUT_TYPE__QUICK_SETTINGS,
+                            qs_shortcut_enabled));
                 }
             }
         } catch (RuntimeException e) {
@@ -5150,16 +5145,19 @@
                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
         final String hardware_shortcut_list = Settings.Secure.getStringForUser(resolver,
                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId);
+        final String qs_shortcut_list = Settings.Secure.getStringForUser(resolver,
+                Settings.Secure.ACCESSIBILITY_QS_TARGETS, userId);
         final boolean hardware_shortcut_dialog_shown = Settings.Secure.getIntForUser(resolver,
                 Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId) == 1;
         final boolean software_shortcut_enabled = !TextUtils.isEmpty(software_shortcut_list);
         final boolean hardware_shortcut_enabled =
                 hardware_shortcut_dialog_shown && !TextUtils.isEmpty(hardware_shortcut_list);
+        final boolean qs_shortcut_enabled = !TextUtils.isEmpty(qs_shortcut_list);
         final boolean triple_tap_shortcut_enabled = Settings.Secure.getIntForUser(resolver,
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId) == 1;
 
         return software_shortcut_enabled || hardware_shortcut_enabled
-                || triple_tap_shortcut_enabled;
+                || triple_tap_shortcut_enabled || qs_shortcut_enabled;
     }
 
     private boolean isAccessibilityFloatingMenuUser(Context context, @UserIdInt int userId) {
@@ -5176,13 +5174,13 @@
     private int convertToAccessibilityShortcutType(int shortcutType) {
         switch (shortcutType) {
             case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR:
-                return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
+                return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_BUTTON;
             case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU:
-                return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+                return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_FLOATING_MENU;
             case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE:
-                return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
+                return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_GESTURE;
             default:
-                return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
+                return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__UNKNOWN_TYPE;
         }
     }
 
diff --git a/services/core/java/com/android/server/stats/stats_flags.aconfig b/services/core/java/com/android/server/stats/stats_flags.aconfig
index c479c6d..6faa273 100644
--- a/services/core/java/com/android/server/stats/stats_flags.aconfig
+++ b/services/core/java/com/android/server/stats/stats_flags.aconfig
@@ -8,11 +8,3 @@
     bug: "309512867"
     is_fixed_read_only: true
 }
-
-flag {
-    name: "stats_pull_network_stats_manager_init_order_fix"
-    namespace: "statsd"
-    description: "Fix the mNetworkStatsManager initialization order"
-    bug: "331989853"
-    is_fixed_read_only: true
-}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 2c67207..4264e91 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -20,9 +20,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
-import static android.app.StatusBarManager.DISABLE2_MASK;
 import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
-import static android.app.StatusBarManager.DISABLE_MASK;
 import static android.app.StatusBarManager.NAV_BAR_MODE_DEFAULT;
 import static android.app.StatusBarManager.NAV_BAR_MODE_KIDS;
 import static android.app.StatusBarManager.NavBarMode;
@@ -222,9 +220,8 @@
         int what1;
         int what2;
         IBinder token;
-        private String mReason;
 
-        DisableRecord(int userId, IBinder token) {
+        public DisableRecord(int userId, IBinder token) {
             this.userId = userId;
             this.token = token;
             try {
@@ -237,12 +234,12 @@
         @Override
         public void binderDied() {
             Slog.i(TAG, "binder died for pkg=" + pkg);
-            StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-            disableForUser(info, token, pkg, userId, "Binder Died");
+            disableForUser(0, token, pkg, userId);
+            disable2ForUser(0, token, pkg, userId);
             token.unlinkToDeath(this, 0);
         }
 
-        public void setFlags(int what, int which, String pkg, String reason) {
+        public void setFlags(int what, int which, String pkg) {
             switch (which) {
                 case 1:
                     what1 = what;
@@ -256,7 +253,6 @@
                     break;
             }
             this.pkg = pkg;
-            this.mReason = reason;
         }
 
         public int getFlags(int which) {
@@ -275,8 +271,8 @@
 
         @Override
         public String toString() {
-            return String.format("userId=%d what1=0x%08X what2=0x%08X pkg=%s token=%s reason=%s",
-                    userId, what1, what2, pkg, token, mReason);
+            return String.format("userId=%d what1=0x%08X what2=0x%08X pkg=%s token=%s",
+                    userId, what1, what2, pkg, token);
         }
     }
 
@@ -954,7 +950,7 @@
 
         if (mBar != null) {
             try {
-                mBar.togglePanel();
+                mBar.toggleNotificationsPanel();
             } catch (RemoteException ex) {
             }
         }
@@ -1184,59 +1180,57 @@
         return mTracingEnabled;
     }
 
-    /**
-     * @deprecated
-     * Disable some features in the status bar.
-     *
-     * This method is deprecated and callers should use
-     * {@link #disableForUser(StatusBarManager.DisableInfo, IBinder, String, int, String)}
-     *
-     * @hide
-     */
-    @Deprecated
+    // TODO(b/117478341): make it aware of multi-display if needed.
     @Override
     public void disable(int what, IBinder token, String pkg) {
-        StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo(what & DISABLE_MASK,
-                what & DISABLE2_MASK);
-        disableForUser(info, token, pkg, mCurrentUserId, null);
-    }
-
-    /**
-     * @deprecated
-     * Disable some features in the status bar.
-     *
-     * This method is deprecated and callers should use
-     * {@link #disableForUser(StatusBarManager.DisableInfo, IBinder, String, int, String)}
-     *
-     * @hide
-     */
-    @Deprecated
-    @Override
-    public void disable2(int what, IBinder token, String pkg) {
-        StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo(what & DISABLE_MASK,
-                what & DISABLE2_MASK);
-        disableForUser(info, token, pkg, mCurrentUserId, null);
+        disableForUser(what, token, pkg, mCurrentUserId);
     }
 
     // TODO(b/117478341): make it aware of multi-display if needed.
     @Override
-    public void disableForUser(StatusBarManager.DisableInfo disableInfo, IBinder token, String pkg,
-            int userId, String reason) {
+    public void disableForUser(int what, IBinder token, String pkg, int userId) {
         enforceStatusBar();
+
         synchronized (mLock) {
-            Pair<Integer, Integer> flags = disableInfo.toFlags();
-            disableLocked(DEFAULT_DISPLAY, userId, flags.first, token, pkg, 1, reason);
-            disableLocked(DEFAULT_DISPLAY, userId, flags.second, token, pkg, 2, reason);
+            disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1);
+        }
+    }
+
+    // TODO(b/117478341): make it aware of multi-display if needed.
+    /**
+     * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
+     * To re-enable everything, pass {@link #DISABLE2_NONE}.
+     *
+     * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
+     */
+    @Override
+    public void disable2(int what, IBinder token, String pkg) {
+        disable2ForUser(what, token, pkg, mCurrentUserId);
+    }
+
+    // TODO(b/117478341): make it aware of multi-display if needed.
+    /**
+     * Disable additional status bar features for a given user. Pass the bitwise-or of the
+     * DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}.
+     *
+     * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
+     */
+    @Override
+    public void disable2ForUser(int what, IBinder token, String pkg, int userId) {
+        enforceStatusBar();
+
+        synchronized (mLock) {
+            disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 2);
         }
     }
 
     private void disableLocked(int displayId, int userId, int what, IBinder token, String pkg,
-            int whichFlag, String reason) {
+            int whichFlag) {
         // It's important that the the callback and the call to mBar get done
         // in the same order when multiple threads are calling this function
         // so they are paired correctly.  The messages on the handler will be
         // handled in the order they were enqueued, but will be outside the lock.
-        manageDisableListLocked(userId, what, token, pkg, whichFlag, reason);
+        manageDisableListLocked(userId, what, token, pkg, whichFlag);
 
         // Ensure state for the current user is applied, even if passed a non-current user.
         final int net1 = gatherDisableActionsLocked(mCurrentUserId, 1);
@@ -1385,7 +1379,7 @@
         // also allows calls from window manager which is in this process.
         enforceStatusBarService();
 
-        final int unknownFlags = flags & ~DISABLE_MASK;
+        final int unknownFlags = flags & ~StatusBarManager.DISABLE_MASK;
         if (unknownFlags != 0) {
             Slog.e(TAG, "Unknown disable flags: 0x" + Integer.toHexString(unknownFlags),
                     new RuntimeException());
@@ -1394,8 +1388,7 @@
         if (SPEW) Slog.d(TAG, "setDisableFlags(0x" + Integer.toHexString(flags) + ")");
 
         synchronized (mLock) {
-            disableLocked(displayId, mCurrentUserId, flags, mSysUiVisToken, cause, 1,
-                    "setDisableFlags");
+            disableLocked(displayId, mCurrentUserId, flags, mSysUiVisToken, cause, 1);
         }
     }
 
@@ -2450,8 +2443,7 @@
     // ================================================================================
 
     // lock on mDisableRecords
-    void manageDisableListLocked(int userId, int what, IBinder token, String pkg, int which,
-            String reason) {
+    void manageDisableListLocked(int userId, int what, IBinder token, String pkg, int which) {
         if (SPEW) {
             Slog.d(TAG, "manageDisableList userId=" + userId
                     + " what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
@@ -2473,7 +2465,7 @@
 
         // Update existing record
         if (record != null) {
-            record.setFlags(what, which, pkg, reason);
+            record.setFlags(what, which, pkg);
             if (record.isEmpty()) {
                 mDisableRecords.remove(i);
                 record.token.unlinkToDeath(record, 0);
@@ -2483,7 +2475,7 @@
 
         // Record doesn't exist, so we create a new one
         record = new DisableRecord(userId, token);
-        record.setFlags(what, which, pkg, reason);
+        record.setFlags(what, which, pkg);
         mDisableRecords.add(record);
     }
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
index adb55b4..d6bf02f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
@@ -16,6 +16,8 @@
 
 import static android.app.StatusBarManager.DEFAULT_SETUP_DISABLE2_FLAGS;
 import static android.app.StatusBarManager.DEFAULT_SETUP_DISABLE_FLAGS;
+import static android.app.StatusBarManager.DISABLE2_NONE;
+import static android.app.StatusBarManager.DISABLE_NONE;
 
 import android.app.StatusBarManager.DisableInfo;
 import android.content.ComponentName;
@@ -25,6 +27,7 @@
 import android.os.RemoteException;
 import android.os.ShellCommand;
 import android.service.quicksettings.TileService;
+import android.util.Pair;
 
 import java.io.PrintWriter;
 
@@ -141,17 +144,25 @@
         String arg = getNextArgRequired();
         String pkg = mContext.getPackageName();
         boolean disable = Boolean.parseBoolean(arg);
-        int userId = Binder.getCallingUserHandle().getIdentifier();
-        DisableInfo info = disable ? new DisableInfo(DEFAULT_SETUP_DISABLE_FLAGS,
-                DEFAULT_SETUP_DISABLE2_FLAGS) : new DisableInfo();
-        mInterface.disableForUser(info, sToken, pkg, userId, "runDisableForSetup");
+
+        if (disable) {
+            mInterface.disable(DEFAULT_SETUP_DISABLE_FLAGS, sToken, pkg);
+            mInterface.disable2(DEFAULT_SETUP_DISABLE2_FLAGS, sToken, pkg);
+        } else {
+            mInterface.disable(DISABLE_NONE, sToken, pkg);
+            mInterface.disable2(DISABLE2_NONE, sToken, pkg);
+        }
+
         return 0;
     }
 
     private int runSendDisableFlag() {
         String pkg = mContext.getPackageName();
-        int userId = Binder.getCallingUserHandle().getIdentifier();
+        int disable1 = DISABLE_NONE;
+        int disable2 = DISABLE2_NONE;
+
         DisableInfo info = new DisableInfo();
+
         String arg = getNextArg();
         while (arg != null) {
             switch (arg) {
@@ -159,7 +170,7 @@
                     info.setSearchDisabled(true);
                     break;
                 case "home":
-                    info.setNavigationHomeDisabled(true);
+                    info.setNagivationHomeDisabled(true);
                     break;
                 case "recents":
                     info.setRecentsDisabled(true);
@@ -186,7 +197,10 @@
             arg = getNextArg();
         }
 
-        mInterface.disableForUser(info, sToken, pkg, userId, "Shell Commands");
+        Pair<Integer, Integer> flagPair = info.toFlags();
+
+        mInterface.disable(flagPair.first, sToken, pkg);
+        mInterface.disable2(flagPair.second, sToken, pkg);
 
         return 0;
     }
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index f6afc52..3393d3e 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -150,7 +150,11 @@
     @Override
     public void onWindowInfosChanged(InputWindowHandle[] windowHandles,
             DisplayInfo[] displayInfos) {
-        mHandler.post(() -> onWindowInfosChangedInternal(windowHandles, displayInfos));
+        if (com.android.server.accessibility.Flags.removeOnWindowInfosChangedHandler()) {
+            onWindowInfosChangedInternal(windowHandles, displayInfos);
+        } else {
+            mHandler.post(() -> onWindowInfosChangedInternal(windowHandles, displayInfos));
+        }
     }
 
     private void onWindowInfosChangedInternal(InputWindowHandle[] windowHandles,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 21e4c96..76e7f53 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -339,7 +339,6 @@
 import android.service.dreams.DreamActivity;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArraySet;
-import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.MergedConfiguration;
@@ -2123,14 +2122,14 @@
         if (mWmService.mFlags.mInsetsDecoupledConfiguration) {
             // When the stable configuration is the default behavior, override for the legacy apps
             // without forward override flag.
-            mResolveConfigHint.mUseOverrideInsetsForStableBounds =
+            mResolveConfigHint.mUseOverrideInsetsForConfig =
                     !info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
                             && !info.isChangeEnabled(
                                     OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION);
         } else {
             // When the stable configuration is not the default behavior, forward overriding the
             // listed apps.
-            mResolveConfigHint.mUseOverrideInsetsForStableBounds =
+            mResolveConfigHint.mUseOverrideInsetsForConfig =
                     info.isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION);
         }
 
@@ -3272,8 +3271,12 @@
         mOccludesParent = occludesParent;
         setMainWindowOpaque(occludesParent);
 
-        if (changed && task != null && !occludesParent) {
-            getRootTask().convertActivityToTranslucent(this);
+        if (changed && task != null) {
+            if (!occludesParent) {
+                getRootTask().convertActivityToTranslucent(this);
+            } else {
+                getRootTask().convertActivityFromTranslucent(this);
+            }
         }
         // Always ensure visibility if this activity doesn't occlude parent, so the
         // {@link #returningOptions} of the activity under this one can be applied in
@@ -4266,6 +4269,12 @@
         getTaskFragment().cleanUpActivityReferences(this);
         clearLastParentBeforePip();
 
+        // Abort and reset state if the scence transition is playing.
+        final Task rootTask = getRootTask();
+        if (rootTask != null) {
+            rootTask.abortTranslucentActivityWaiting(this);
+        }
+
         // Clean up the splash screen if it was still displayed.
         cleanUpSplashScreen();
 
@@ -5672,6 +5681,8 @@
                 } else if (mTransitionController.inFinishingTransition(this)) {
                     mTransitionChangeFlags |= FLAGS_IS_OCCLUDED_NO_ANIMATION;
                 }
+            } else {
+                mTransitionChangeFlags &= ~FLAG_IS_OCCLUDED;
             }
             return;
         }
@@ -6519,8 +6530,8 @@
             // and the token could be null.
             return;
         }
-        if (r.mDisplayContent.mDisplayRotationCompatPolicy != null) {
-            r.mDisplayContent.mDisplayRotationCompatPolicy.onActivityRefreshed(r);
+        if (r.mDisplayContent.mActivityRefresher != null) {
+            r.mDisplayContent.mActivityRefresher.onActivityRefreshed(r);
         }
     }
 
@@ -8480,7 +8491,7 @@
         mCompatDisplayInsets =
                 new CompatDisplayInsets(
                         mDisplayContent, this, letterboxedContainerBounds,
-                        mResolveConfigHint.mUseOverrideInsetsForStableBounds);
+                        mResolveConfigHint.mUseOverrideInsetsForConfig);
     }
 
     private void clearSizeCompatModeAttributes() {
@@ -8560,8 +8571,6 @@
         final int parentWindowingMode =
                 newParentConfiguration.windowConfiguration.getWindowingMode();
 
-        applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig);
-
         // Bubble activities should always fill their parent and should not be letterboxed.
         final boolean isFixedOrientationLetterboxAllowed = !getLaunchedFromBubble()
                 && (parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
@@ -8661,6 +8670,8 @@
             resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
         }
 
+        applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig);
+
         logAppCompatState();
     }
 
@@ -8679,14 +8690,13 @@
         if (mDisplayContent == null) {
             return;
         }
-        final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
         int rotation = newParentConfiguration.windowConfiguration.getRotation();
         if (rotation == ROTATION_UNDEFINED && !isFixedRotationTransforming()) {
             rotation = mDisplayContent.getRotation();
         }
-        if (!mResolveConfigHint.mUseOverrideInsetsForStableBounds
-                || getCompatDisplayInsets() != null || isFloating(parentWindowingMode)
-                || rotation == ROTATION_UNDEFINED) {
+        if (!mResolveConfigHint.mUseOverrideInsetsForConfig
+                || getCompatDisplayInsets() != null || shouldCreateCompatDisplayInsets()
+                || isFloating(parentWindowingMode) || rotation == ROTATION_UNDEFINED) {
             // If the insets configuration decoupled logic is not enabled for the app, or the app
             // already has a compat override, or the context doesn't contain enough info to
             // calculate the override, skip the override.
@@ -8703,53 +8713,7 @@
         }
 
         // Override starts here.
-        final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        final int dw = rotated ? mDisplayContent.mBaseDisplayHeight
-                : mDisplayContent.mBaseDisplayWidth;
-        final int dh = rotated ? mDisplayContent.mBaseDisplayWidth
-                : mDisplayContent.mBaseDisplayHeight;
-        final  Rect nonDecorInsets = mDisplayContent.getDisplayPolicy()
-                .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets;
-        // This should be the only place override the configuration for ActivityRecord. Override
-        // the value if not calculated yet.
-        Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-        if (outAppBounds == null || outAppBounds.isEmpty()) {
-            inOutConfig.windowConfiguration.setAppBounds(parentBounds);
-            outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-            outAppBounds.inset(nonDecorInsets);
-        }
-        float density = inOutConfig.densityDpi;
-        if (density == Configuration.DENSITY_DPI_UNDEFINED) {
-            density = newParentConfiguration.densityDpi;
-        }
-        density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
-        if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
-            final int overrideScreenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
-            inOutConfig.screenWidthDp = overrideScreenWidthDp;
-        }
-        if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
-            final int overrideScreenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
-            inOutConfig.screenHeightDp = overrideScreenHeightDp;
-        }
-        if (inOutConfig.smallestScreenWidthDp
-                == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
-                && parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
-            // For the case of PIP transition and multi-window environment, the
-            // smallestScreenWidthDp is handled already. Override only if the app is in
-            // fullscreen.
-            final DisplayInfo info = new DisplayInfo(mDisplayContent.getDisplayInfo());
-            mDisplayContent.computeSizeRanges(info, rotated, dw, dh,
-                    mDisplayContent.getDisplayMetrics().density,
-                    inOutConfig, true /* overrideConfig */);
-        }
-
-        // It's possible that screen size will be considered in different orientation with or
-        // without considering the system bar insets. Override orientation as well.
-        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
-            inOutConfig.orientation =
-                    (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
-                            ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
-        }
+        computeConfigByResolveHint(inOutConfig, newParentConfiguration);
     }
 
     private void computeConfigByResolveHint(@NonNull Configuration resolvedConfig,
@@ -9005,7 +8969,7 @@
         if (mDisplayContent == null) {
             return true;
         }
-        if (!mResolveConfigHint.mUseOverrideInsetsForStableBounds) {
+        if (!mResolveConfigHint.mUseOverrideInsetsForConfig) {
             // No insets should be considered any more.
             return true;
         }
@@ -9024,7 +8988,7 @@
         final Task task = getTask();
         task.calculateInsetFrames(outNonDecorBounds /* outNonDecorBounds */,
                 outStableBounds /* outStableBounds */, parentBounds /* bounds */, di,
-                mResolveConfigHint.mUseOverrideInsetsForStableBounds);
+                mResolveConfigHint.mUseOverrideInsetsForConfig);
         final int orientationWithInsets = outStableBounds.height() >= outStableBounds.width()
                 ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
         // If orientation does not match the orientation with insets applied, then a
@@ -9081,7 +9045,7 @@
                 getResolvedOverrideConfiguration().windowConfiguration.getBounds();
         final int stableBoundsOrientation = stableBounds.width() > stableBounds.height()
                 ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
-        final int parentOrientation = mResolveConfigHint.mUseOverrideInsetsForStableBounds
+        final int parentOrientation = mResolveConfigHint.mUseOverrideInsetsForConfig
                 ? stableBoundsOrientation : newParentConfig.orientation;
 
         // If the activity requires a different orientation (either by override or activityInfo),
@@ -9106,7 +9070,7 @@
             return;
         }
 
-        final Rect parentAppBounds = mResolveConfigHint.mUseOverrideInsetsForStableBounds
+        final Rect parentAppBounds = mResolveConfigHint.mUseOverrideInsetsForConfig
                 ? outNonDecorBounds : newParentConfig.windowConfiguration.getAppBounds();
         // TODO(b/182268157): Explore using only one type of parentBoundsWithInsets, either app
         // bounds or stable bounds to unify aspect ratio logic.
@@ -10032,7 +9996,7 @@
             } else {
                 scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo);
             }
-            notifyDisplayCompatPolicyAboutConfigurationChange(
+            notifyActivityRefresherAboutConfigurationChange(
                     mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig);
             return true;
         }
@@ -10099,18 +10063,18 @@
         } else {
             scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo);
         }
-        notifyDisplayCompatPolicyAboutConfigurationChange(
+        notifyActivityRefresherAboutConfigurationChange(
                 mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig);
         return true;
     }
 
-    private void notifyDisplayCompatPolicyAboutConfigurationChange(
+    private void notifyActivityRefresherAboutConfigurationChange(
             Configuration newConfig, Configuration lastReportedConfig) {
-        if (mDisplayContent.mDisplayRotationCompatPolicy == null
+        if (mDisplayContent.mActivityRefresher == null
                 || !shouldBeResumed(/* activeActivity */ null)) {
             return;
         }
-        mDisplayContent.mDisplayRotationCompatPolicy.onActivityConfigurationChanging(
+        mDisplayContent.mActivityRefresher.onActivityConfigurationChanging(
                 this, newConfig, lastReportedConfig);
     }
 
@@ -11111,7 +11075,7 @@
      * Otherwise, return the creation time of the top window.
      */
     long getLastWindowCreateTime() {
-        final WindowState window = getWindow(win -> true);
+        final WindowState window = getWindow(alwaysTruePredicate());
         return window != null && window.mAttrs.type != TYPE_BASE_APPLICATION
                 ? window.getCreateTime()
                 : createTime;
diff --git a/services/core/java/com/android/server/wm/ActivityRefresher.java b/services/core/java/com/android/server/wm/ActivityRefresher.java
new file mode 100644
index 0000000..23a9708
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityRefresher.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wm;
+
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
+
+import android.annotation.NonNull;
+import android.app.servertransaction.RefreshCallbackItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.RemoteException;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Class that refreshes the activity (through stop/pause -> resume) based on configuration change.
+ *
+ * <p>This class queries all of its {@link Evaluator}s and restarts the activity if any of them
+ * return {@code true} in {@link Evaluator#shouldRefreshActivity}. {@link ActivityRefresher} cycles
+ * through either stop or pause and then resume, based on the global config and per-app override.
+ */
+class ActivityRefresher {
+    // Delay for ensuring that onActivityRefreshed is always called after an activity refresh. The
+    // client process may not always report the event back to the server, such as process is
+    // crashed or got killed.
+    private static final long REFRESH_CALLBACK_TIMEOUT_MS = 2000L;
+
+    @NonNull private final WindowManagerService mWmService;
+    @NonNull private final Handler mHandler;
+    @NonNull private final ArrayList<Evaluator> mEvaluators = new ArrayList<>();
+
+    ActivityRefresher(@NonNull WindowManagerService wmService, @NonNull Handler handler) {
+        mWmService = wmService;
+        mHandler = handler;
+    }
+
+    void addEvaluator(@NonNull Evaluator evaluator) {
+        mEvaluators.add(evaluator);
+    }
+
+    void removeEvaluator(@NonNull Evaluator evaluator) {
+        mEvaluators.remove(evaluator);
+    }
+
+    /**
+     * "Refreshes" activity by going through "stopped -> resumed" or "paused -> resumed" cycle.
+     * This allows to clear cached values in apps (e.g. display or camera rotation) that influence
+     * camera preview and can lead to sideways or stretching issues persisting even after force
+     * rotation.
+     */
+    void onActivityConfigurationChanging(@NonNull ActivityRecord activity,
+            @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) {
+        if (!shouldRefreshActivity(activity, newConfig, lastReportedConfig)) {
+            return;
+        }
+
+        final boolean cycleThroughStop =
+                mWmService.mLetterboxConfiguration
+                        .isCameraCompatRefreshCycleThroughStopEnabled()
+                        && !activity.mLetterboxUiController
+                        .shouldRefreshActivityViaPauseForCameraCompat();
+
+        activity.mLetterboxUiController.setIsRefreshRequested(true);
+        ProtoLog.v(WM_DEBUG_STATES,
+                "Refreshing activity for freeform camera compatibility treatment, "
+                        + "activityRecord=%s", activity);
+        final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(
+                activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE);
+        final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(
+                activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
+        try {
+            activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
+                    activity.app.getThread(), refreshCallbackItem, resumeActivityItem);
+            mHandler.postDelayed(() -> {
+                synchronized (mWmService.mGlobalLock) {
+                    onActivityRefreshed(activity);
+                }
+            }, REFRESH_CALLBACK_TIMEOUT_MS);
+        } catch (RemoteException e) {
+            activity.mLetterboxUiController.setIsRefreshRequested(false);
+        }
+    }
+
+    boolean isActivityRefreshing(@NonNull ActivityRecord activity) {
+        return activity.mLetterboxUiController.isRefreshRequested();
+    }
+
+    void onActivityRefreshed(@NonNull ActivityRecord activity) {
+        // TODO(b/333060789): can we tell that refresh did not happen by observing the activity
+        //  state?
+        activity.mLetterboxUiController.setIsRefreshRequested(false);
+    }
+
+    private boolean shouldRefreshActivity(@NonNull ActivityRecord activity,
+            @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) {
+        return mWmService.mLetterboxConfiguration.isCameraCompatRefreshEnabled()
+                && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat()
+                && ArrayUtils.find(mEvaluators.toArray(), evaluator ->
+                ((Evaluator) evaluator)
+                        .shouldRefreshActivity(activity, newConfig, lastReportedConfig)) != null;
+    }
+
+    /**
+     * Interface for classes that would like to refresh the recently updated activity, based on the
+     * configuration change.
+     */
+    interface Evaluator {
+        boolean shouldRefreshActivity(@NonNull ActivityRecord activity,
+                @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
index 3609837..ed07afd 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
@@ -30,10 +30,12 @@
     @Override
     void putSnapshot(ActivityRecord ar, TaskSnapshot snapshot) {
         final int hasCode = System.identityHashCode(ar);
+        snapshot.addReference(TaskSnapshot.REFERENCE_CACHE);
         synchronized (mLock) {
             final CacheEntry entry = mRunningCache.get(hasCode);
             if (entry != null) {
                 mAppIdMap.remove(entry.topApp);
+                entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE);
             }
             mAppIdMap.put(ar, hasCode);
             mRunningCache.put(hasCode, new CacheEntry(snapshot, ar));
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 3303367..08aeede 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1768,6 +1768,7 @@
             if (!avoidMoveToFront() && (mService.mHomeProcess == null
                     || mService.mHomeProcess.mUid != realCallingUid)
                     && (prevTopTask != null && prevTopTask.isActivityTypeHomeOrRecents())
+                    && !targetTask.isActivityTypeHomeOrRecents()
                     && r.mTransitionController.isTransientHide(targetTask)) {
                 mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY;
             }
@@ -2113,7 +2114,6 @@
         if (hostTask == null || targetTask != hostTask) {
             return EMBEDDING_DISALLOWED_NEW_TASK;
         }
-
         return taskFragment.isAllowedToEmbedActivity(starting);
     }
 
@@ -2167,7 +2167,7 @@
             // We don't need to start a new activity, and the client said not to do anything
             // if that is the case, so this is it!  And for paranoia, make sure we have
             // correctly resumed the top activity.
-            if (!mMovedToFront && mDoResume) {
+            if (!mMovedToFront && mDoResume && !avoidMoveToFront()) {
                 ProtoLog.d(WM_DEBUG_TASKS, "Bring to front target: %s from %s", mTargetRootTask,
                         targetTaskTop);
                 mTargetRootTask.moveToFront("intentActivityFound");
@@ -2196,7 +2196,7 @@
         if (mMovedToFront) {
             // We moved the task to front, use starting window to hide initial drawn delay.
             targetTaskTop.showStartingWindow(true /* taskSwitch */);
-        } else if (mDoResume) {
+        } else if (mDoResume && !avoidMoveToFront()) {
             // Make sure the root task and its belonging display are moved to topmost.
             mTargetRootTask.moveToFront("intentActivityFound");
         }
@@ -2961,23 +2961,9 @@
                 sendCanNotEmbedActivityError(mInTaskFragment, embeddingCheckResult);
             }
         } else {
-            TaskFragment candidateTf = mAddingToTaskFragment != null ? mAddingToTaskFragment : null;
+            TaskFragment candidateTf = mAddingToTaskFragment;
             if (candidateTf == null) {
-                // Puts the activity on the top-most non-isolated navigation TF, unless the
-                // activity is launched from the same TF.
-                final TaskFragment sourceTaskFragment =
-                        mSourceRecord != null ? mSourceRecord.getTaskFragment() : null;
-                final ActivityRecord top = task.getActivity(r -> {
-                    if (!r.canBeTopRunning()) {
-                        return false;
-                    }
-                    final TaskFragment taskFragment = r.getTaskFragment();
-                    return !taskFragment.isIsolatedNav() || (sourceTaskFragment != null
-                            && sourceTaskFragment == taskFragment);
-                });
-                if (top != null) {
-                    candidateTf = top.getTaskFragment();
-                }
+                candidateTf = findCandidateTaskFragment(task);
             }
             if (candidateTf != null && candidateTf.isEmbedded()
                     && canEmbedActivity(candidateTf, mStartActivity, task) == EMBEDDING_ALLOWED) {
@@ -2995,6 +2981,50 @@
     }
 
     /**
+     * Finds a candidate TaskFragment in {@code task} to launch activity, or returns {@code null}
+     * if there's no such a TaskFragment.
+     */
+    @Nullable
+    private TaskFragment findCandidateTaskFragment(@NonNull Task task) {
+        final TaskFragment sourceTaskFragment =
+                mSourceRecord != null ? mSourceRecord.getTaskFragment() : null;
+        for (int i = task.getChildCount() - 1; i >= 0; --i) {
+            final WindowContainer<?> wc = task.getChildAt(i);
+            final ActivityRecord activity = wc.asActivityRecord();
+            if (activity != null) {
+                if (activity.finishing) {
+                    continue;
+                }
+                // Early return if the top child is an Activity.
+                return null;
+            }
+            final TaskFragment taskFragment = wc.asTaskFragment();
+            if (taskFragment == null || taskFragment.isRemovalRequested()) {
+                // Skip if the TaskFragment is going to be finished.
+                continue;
+            }
+            if (taskFragment.getActivity(ActivityRecord::canBeTopRunning) == null) {
+                // Skip if there's no activity in this TF can be top running.
+                continue;
+            }
+            if (taskFragment.isIsolatedNav()) {
+                // Stop here if we reach an isolated navigated TF.
+                return null;
+            }
+            if (sourceTaskFragment != null && sourceTaskFragment == taskFragment) {
+                // Choose the taskFragment launched from even if it's pinned.
+                return taskFragment;
+            }
+            if (taskFragment.isPinned()) {
+                // Skip the pinned TaskFragment.
+                continue;
+            }
+            return taskFragment;
+        }
+        return null;
+    }
+
+    /**
      * Notifies the client side that {@link #mStartActivity} cannot be embedded to
      * {@code taskFragment}.
      */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 237003a..3aa63af 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -7418,7 +7418,8 @@
                     FEATURE_LEANBACK);
             final boolean isArc = arcFeature != null && arcFeature.version >= 0;
             final boolean isTv = tvFeature != null && tvFeature.version >= 0;
-            sIsPip2ExperimentEnabled = SystemProperties.getBoolean("wm_shell.pip2", false)
+            sIsPip2ExperimentEnabled = SystemProperties.getBoolean(
+                    "persist.wm_shell.pip2", false)
                     || (Flags.enablePip2Implementation() && !isArc && !isTv);
         }
         return sIsPip2ExperimentEnabled;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index c74284e..f06d3af 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1704,6 +1704,15 @@
         final Transition transit = task.mTransitionController.requestCloseTransitionIfNeeded(task);
         if (transit != null) {
             transit.collectClose(task);
+            if (!task.mTransitionController.useFullReadyTracking()) {
+                // If a transition was created here, it means this is an isolated removeTask. It's
+                // possible for there to be no consequent operations (eg. this is a multiwindow task
+                // closing so nothing becomes visible in response) so we must "touch" the old ready
+                // tracker so that it doesn't get stuck. However, since the old ready tracker
+                // doesn't support multiple conditions, we have to touch it here at the beginning
+                // before anything that may need it to wait (setReady(false)).
+                transit.setReady(task, true);
+            }
         } else if (task.mTransitionController.isCollecting()) {
             task.mTransitionController.getCollectingTransition().collectClose(task);
         }
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index c9703d8..0e4f033 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -1732,7 +1732,10 @@
             // The activity was detached from hierarchy.
             return;
         }
-        activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
+
+        if (activity.mDisplayContent.isFixedRotationLaunchingApp(activity)) {
+            activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
+        }
 
         // Restore the launch-behind state.
         activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index eb1f3b4..f7910b0 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityOptions.BackgroundActivityStartMode;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -105,6 +106,7 @@
     static final String AUTO_OPT_IN_NOT_PENDING_INTENT = "notPendingIntent";
     static final String AUTO_OPT_IN_CALL_FOR_RESULT = "callForResult";
     static final String AUTO_OPT_IN_SAME_UID = "sameUid";
+    static final String AUTO_OPT_IN_COMPAT = "compatibility";
 
     /** If enabled the creator will not allow BAL on its behalf by default. */
     @ChangeId
@@ -303,6 +305,10 @@
             } else if (callingUid == realCallingUid && !balRequireOptInSameUid()) {
                 mAutoOptInReason = AUTO_OPT_IN_SAME_UID;
                 mAutoOptInCaller = false;
+            } else if (realCallerBackgroundActivityStartMode
+                    == MODE_BACKGROUND_ACTIVITY_START_COMPAT) {
+                mAutoOptInReason = AUTO_OPT_IN_COMPAT;
+                mAutoOptInCaller = false;
             } else {
                 mAutoOptInReason = null;
                 mAutoOptInCaller = false;
@@ -1695,6 +1701,7 @@
             return false;
         }
         if (state.mBalAllowedByPiSender.allowsBackgroundActivityStarts()
+                && state.mResultForRealCaller != null
                 && state.mResultForRealCaller.getRawCode() == BAL_ALLOW_VISIBLE_WINDOW) {
             return false;
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c9a5e71..e49cb38 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -478,6 +478,8 @@
     final DisplayRotationCompatPolicy mDisplayRotationCompatPolicy;
     @Nullable
     final CameraStateMonitor mCameraStateMonitor;
+    @Nullable
+    final ActivityRefresher mActivityRefresher;
 
     DisplayFrames mDisplayFrames;
     final DisplayUpdater mDisplayUpdater;
@@ -1233,13 +1235,15 @@
                 mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabledAtBuildTime();
         if (shouldCreateDisplayRotationCompatPolicy) {
             mCameraStateMonitor = new CameraStateMonitor(this, mWmService.mH);
+            mActivityRefresher = new ActivityRefresher(mWmService, mWmService.mH);
             mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(
-                    this, mWmService.mH, mCameraStateMonitor);
+                    this, mCameraStateMonitor, mActivityRefresher);
 
             mCameraStateMonitor.startListeningToCameraState();
         } else {
             // These are to satisfy the `final` check.
             mCameraStateMonitor = null;
+            mActivityRefresher = null;
             mDisplayRotationCompatPolicy = null;
         }
 
@@ -2755,7 +2759,7 @@
 
     @Nullable
     Task getTopRootTask() {
-        return getRootTask(t -> true);
+        return getRootTask(alwaysTruePredicate());
     }
 
     /**
@@ -5009,7 +5013,7 @@
 
         // This should be called after the insets have been dispatched to clients and we have
         // committed finish drawing windows.
-        mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
+        mInsetsStateController.getImeSourceProvider().checkAndStartShowImePostLayout();
 
         mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
         if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index eacf9a3..e0cc064 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -18,8 +18,6 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
-import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
@@ -32,19 +30,14 @@
 import static android.view.Display.TYPE_INTERNAL;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
 import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_CAMERA_COMPAT;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
-import android.app.servertransaction.RefreshCallbackItem;
-import android.app.servertransaction.ResumeActivityItem;
 import android.content.pm.ActivityInfo.ScreenOrientation;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.os.Handler;
-import android.os.RemoteException;
 import android.widget.Toast;
 
 import com.android.internal.R;
@@ -64,48 +57,38 @@
  * R.bool.config_isWindowManagerCameraCompatTreatmentEnabled} is {@code true}.
  */
  // TODO(b/261444714): Consider moving Camera-specific logic outside of the WM Core path
-class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStateListener {
+final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStateListener,
+        ActivityRefresher.Evaluator {
 
-    // Delay for updating display rotation after Camera connection is closed. Needed to avoid
-    // rotation flickering when an app is flipping between front and rear cameras or when size
-    // compat mode is restarted.
-    // TODO(b/263114289): Consider associating this delay with a specific activity so that if
-    // the new non-camera activity started on top of the camer one we can rotate faster.
-    private static final int CAMERA_CLOSED_ROTATION_UPDATE_DELAY_MS = 2000;
-    // Delay for updating display rotation after Camera connection is opened. This delay is
-    // selected to be long enough to avoid conflicts with transitions on the app's side.
-    // Using a delay < CAMERA_CLOSED_ROTATION_UPDATE_DELAY_MS to avoid flickering when an app
-    // is flipping between front and rear cameras (in case requested orientation changes at
-    // runtime at the same time) or when size compat mode is restarted.
-    private static final int CAMERA_OPENED_ROTATION_UPDATE_DELAY_MS =
-            CAMERA_CLOSED_ROTATION_UPDATE_DELAY_MS / 2;
-    // Delay for ensuring that onActivityRefreshed is always called after an activity refresh. The
-    // client process may not always report the event back to the server, such as process is
-    // crashed or got killed.
-    private static final int REFRESH_CALLBACK_TIMEOUT_MS = 2000;
-
+    @NonNull
     private final DisplayContent mDisplayContent;
+    @NonNull
     private final WindowManagerService mWmService;
+    @NonNull
     private final CameraStateMonitor mCameraStateMonitor;
-    private final Handler mHandler;
+    @NonNull
+    private final ActivityRefresher mActivityRefresher;
 
     @ScreenOrientation
     private int mLastReportedOrientation = SCREEN_ORIENTATION_UNSET;
 
-    DisplayRotationCompatPolicy(@NonNull DisplayContent displayContent, Handler handler,
-            @NonNull CameraStateMonitor cameraStateMonitor) {
+    DisplayRotationCompatPolicy(@NonNull DisplayContent displayContent,
+            @NonNull CameraStateMonitor cameraStateMonitor,
+            @NonNull ActivityRefresher activityRefresher) {
         // This constructor is called from DisplayContent constructor. Don't use any fields in
         // DisplayContent here since they aren't guaranteed to be set.
-        mHandler = handler;
         mDisplayContent = displayContent;
         mWmService = displayContent.mWmService;
         mCameraStateMonitor = cameraStateMonitor;
         mCameraStateMonitor.addCameraStateListener(this);
+        mActivityRefresher = activityRefresher;
+        mActivityRefresher.addEvaluator(this);
     }
 
     /** Releases camera state listener. */
     void dispose() {
         mCameraStateMonitor.removeCameraStateListener(this);
+        mActivityRefresher.removeEvaluator(this);
     }
 
     /**
@@ -169,47 +152,6 @@
     }
 
     /**
-     * "Refreshes" activity by going through "stopped -> resumed" or "paused -> resumed" cycle.
-     * This allows to clear cached values in apps (e.g. display or camera rotation) that influence
-     * camera preview and can lead to sideways or stretching issues persisting even after force
-     * rotation.
-     */
-    void onActivityConfigurationChanging(ActivityRecord activity, Configuration newConfig,
-            Configuration lastReportedConfig) {
-        if (!isTreatmentEnabledForDisplay()
-                || !mWmService.mLetterboxConfiguration.isCameraCompatRefreshEnabled()
-                || !shouldRefreshActivity(activity, newConfig, lastReportedConfig)) {
-            return;
-        }
-        boolean cycleThroughStop =
-                mWmService.mLetterboxConfiguration
-                        .isCameraCompatRefreshCycleThroughStopEnabled()
-                && !activity.mLetterboxUiController
-                        .shouldRefreshActivityViaPauseForCameraCompat();
-        try {
-            activity.mLetterboxUiController.setIsRefreshAfterRotationRequested(true);
-            ProtoLog.v(WM_DEBUG_STATES,
-                    "Refreshing activity for camera compatibility treatment, "
-                            + "activityRecord=%s", activity);
-            final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(
-                    activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE);
-            final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(
-                    activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
-            activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
-                    activity.app.getThread(), refreshCallbackItem, resumeActivityItem);
-            mHandler.postDelayed(
-                    () -> onActivityRefreshed(activity),
-                    REFRESH_CALLBACK_TIMEOUT_MS);
-        } catch (RemoteException e) {
-            activity.mLetterboxUiController.setIsRefreshAfterRotationRequested(false);
-        }
-    }
-
-    void onActivityRefreshed(@NonNull ActivityRecord activity) {
-        activity.mLetterboxUiController.setIsRefreshAfterRotationRequested(false);
-    }
-
-    /**
      * Notifies that animation in {@link ScreenRotationAnimation} has finished.
      *
      * <p>This class uses this signal as a trigger for notifying the user about forced rotation
@@ -276,14 +218,16 @@
 
     // Refreshing only when configuration changes after rotation or camera split screen aspect ratio
     // treatment is enabled
-    private boolean shouldRefreshActivity(ActivityRecord activity, Configuration newConfig,
-            Configuration lastReportedConfig) {
+    @Override
+    public boolean shouldRefreshActivity(@NonNull ActivityRecord activity,
+            @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) {
         final boolean displayRotationChanged = (newConfig.windowConfiguration.getDisplayRotation()
                 != lastReportedConfig.windowConfiguration.getDisplayRotation());
-        return (displayRotationChanged
-                || activity.mLetterboxUiController.isCameraCompatSplitScreenAspectRatioAllowed())
+        return isTreatmentEnabledForDisplay()
                 && isTreatmentEnabledForActivity(activity)
-                && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat();
+                && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat()
+                && (displayRotationChanged
+                || activity.mLetterboxUiController.isCameraCompatSplitScreenAspectRatioAllowed());
     }
 
     /**
@@ -310,7 +254,6 @@
                 && activity.mLetterboxUiController.shouldForceRotateForCameraCompat();
     }
 
-
     /**
      * Whether camera compat treatment is applicable for the given activity.
      *
@@ -429,6 +372,6 @@
                 || !mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) {
             return false;
         }
-        return topActivity.mLetterboxUiController.isRefreshAfterRotationRequested();
+        return mActivityRefresher.isActivityRefreshing(topActivity);
     }
 }
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 092ff3d..e03ff688 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -24,7 +24,6 @@
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.ImeInsetsSourceProviderProto.IME_TARGET_FROM_IME;
 import static com.android.server.wm.ImeInsetsSourceProviderProto.INSETS_SOURCE_PROVIDER;
-import static com.android.server.wm.ImeInsetsSourceProviderProto.IS_IME_LAYOUT_DRAWN;
 import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
 
 import android.annotation.NonNull;
@@ -52,19 +51,26 @@
 
     private static final String TAG = ImeInsetsSourceProvider.class.getSimpleName();
 
-    /** The token tracking the current IME request or {@code null} otherwise. */
+    /** The token tracking the show IME request, non-null only while a show request is pending. */
     @Nullable
-    private ImeTracker.Token mImeRequesterStatsToken;
+    private ImeTracker.Token mStatsToken;
+    /** The target that requested to show the IME, non-null only while a show request is pending. */
+    @Nullable
     private InsetsControlTarget mImeRequester;
-    private Runnable mShowImeRunner;
-    private boolean mIsImeLayoutDrawn;
+    /** @see #isImeShowing() */
     private boolean mImeShowing;
+    /** The latest received insets source. */
     private final InsetsSource mLastSource = new InsetsSource(ID_IME, WindowInsets.Type.ime());
 
     /** @see #setFrozen(boolean) */
     private boolean mFrozen;
 
-    /** @see #setServerVisible(boolean) */
+    /**
+     * The server visibility of the source provider's window container. This is out of sync with
+     * {@link InsetsSourceProvider#mServerVisible} while {@link #mFrozen} is {@code true}.
+     *
+     * @see #setServerVisible
+     */
     private boolean mServerVisible;
 
     ImeInsetsSourceProvider(@NonNull InsetsSource source,
@@ -73,6 +79,7 @@
         super(source, stateController, displayContent);
     }
 
+    @Nullable
     @Override
     InsetsSourceControl getControl(InsetsControlTarget target) {
         final InsetsSourceControl control = super.getControl(target);
@@ -124,9 +131,9 @@
     /**
      * Freeze IME insets source state when required.
      *
-     * When setting {@param frozen} as {@code true}, the IME insets provider will freeze the
+     * <p>When setting {@param frozen} as {@code true}, the IME insets provider will freeze the
      * current IME insets state and pending the IME insets state update until setting
-     * {@param frozen} as {@code false}.
+     * {@param frozen} as {@code false}.</p>
      */
     void setFrozen(boolean frozen) {
         if (mFrozen == frozen) {
@@ -223,27 +230,29 @@
     /**
      * Called from {@link WindowManagerInternal#showImePostLayout}
      * when {@link android.inputmethodservice.InputMethodService} requests to show IME
-     * on {@param imeTarget}.
+     * on the given control target.
      *
-     * @param imeTarget imeTarget on which IME request is coming from.
+     * @param imeTarget  the control target on which the IME request is coming from.
      * @param statsToken the token tracking the current IME request.
      */
-    void scheduleShowImePostLayout(InsetsControlTarget imeTarget,
+    void scheduleShowImePostLayout(@NonNull InsetsControlTarget imeTarget,
             @NonNull ImeTracker.Token statsToken) {
-        if (mImeRequesterStatsToken != null) {
-            // Cancel the pre-existing stats token, if any.
-            // Log state on pre-existing request cancel.
-            logShowImePostLayoutState(false /* aborted */);
-            ImeTracker.forLogging().onCancelled(
-                    mImeRequesterStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
+        if (mImeRequester == null) {
+            // Start tracing only on initial scheduled show IME request, to record end-to-end time.
+            Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
+        } else {
+            // We already have a scheduled show IME request, cancel the previous statsToken and
+            // continue with the new one.
+            logIsScheduledAndReadyToShowIme(false /* aborted */);
+            ImeTracker.forLogging().onCancelled(mStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
         }
-        mImeRequesterStatsToken = statsToken;
-        boolean targetChanged = isTargetChangedWithinActivity(imeTarget);
+        final boolean targetChanged = isTargetChangedWithinActivity(imeTarget);
         mImeRequester = imeTarget;
+        mStatsToken = statsToken;
         if (targetChanged) {
             // target changed, check if new target can show IME.
             ProtoLog.d(WM_DEBUG_IME, "IME target changed within ActivityRecord");
-            checkShowImePostLayout();
+            checkAndStartShowImePostLayout();
             // if IME cannot be shown at this time, it is scheduled to be shown.
             // once window that called IMM.showSoftInput() and DisplayContent's ImeTarget match,
             // it will be shown.
@@ -252,79 +261,58 @@
 
         ProtoLog.d(WM_DEBUG_IME, "Schedule IME show for %s", mImeRequester.getWindow() == null
                 ? mImeRequester : mImeRequester.getWindow().getName());
-        mShowImeRunner = () -> {
-            ImeTracker.forLogging().onProgress(mImeRequesterStatsToken,
-                    ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
-            ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner");
-            // Target should still be the same.
-            if (isReadyToShowIme()) {
-                ImeTracker.forLogging().onProgress(mImeRequesterStatsToken,
-                        ImeTracker.PHASE_WM_SHOW_IME_READY);
-                final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_CONTROL);
-
-                ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s",
-                        target.getWindow() != null ? target.getWindow().getName() : "");
-                setImeShowing(true);
-                target.showInsets(WindowInsets.Type.ime(), true /* fromIme */,
-                        mImeRequesterStatsToken);
-                Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
-                if (target != mImeRequester && mImeRequester != null) {
-                    ProtoLog.w(WM_DEBUG_IME,
-                            "showInsets(ime) was requested by different window: %s ",
-                            (mImeRequester.getWindow() != null
-                                    ? mImeRequester.getWindow().getName() : ""));
-                }
-            } else {
-                ImeTracker.forLogging().onFailed(mImeRequesterStatsToken,
-                        ImeTracker.PHASE_WM_SHOW_IME_READY);
-            }
-            // Clear token here so we don't report an error in abortShowImePostLayout().
-            mImeRequesterStatsToken = null;
-            abortShowImePostLayout();
-        };
         mDisplayContent.mWmService.requestTraversal();
     }
 
-    void checkShowImePostLayout() {
-        if (mWindowContainer == null) {
+    /**
+     * Checks whether there is a previously scheduled show IME request and we are ready to show,
+     * in which case also start handling the request.
+     */
+    void checkAndStartShowImePostLayout() {
+        if (!isScheduledAndReadyToShowIme()) {
+            // This can later become ready, so we don't want to cancel the pending request here.
             return;
         }
-        WindowState windowState =  mWindowContainer.asWindowState();
-        if (windowState == null) {
-            throw new IllegalArgumentException("IME insets must be provided by a window.");
+
+        ImeTracker.forLogging().onProgress(mStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
+        ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner");
+
+        final InsetsControlTarget target = getControlTarget();
+
+        ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s",
+                target.getWindow() != null ? target.getWindow().getName() : "");
+        setImeShowing(true);
+        target.showInsets(WindowInsets.Type.ime(), true /* fromIme */, mStatsToken);
+        Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
+        if (target != mImeRequester) {
+            ProtoLog.w(WM_DEBUG_IME, "showInsets(ime) was requested by different window: %s ",
+                    (mImeRequester.getWindow() != null ? mImeRequester.getWindow().getName() : ""));
         }
-        // check if IME is drawn
-        if (mIsImeLayoutDrawn
-                || (isReadyToShowIme()
-                && windowState.isDrawn()
-                && !windowState.mGivenInsetsPending)) {
-            mIsImeLayoutDrawn = true;
-            // show IME if InputMethodService requested it to be shown.
-            if (mShowImeRunner != null) {
-                mShowImeRunner.run();
-            }
-        }
+        resetShowImePostLayout();
     }
 
-    /**
-     * Abort any pending request to show IME post layout.
-     */
+    /** Aborts the previously scheduled show IME request. */
     void abortShowImePostLayout() {
-        ProtoLog.d(WM_DEBUG_IME, "abortShowImePostLayout");
-        if (mImeRequesterStatsToken != null) {
-            // Log state on abort.
-            logShowImePostLayoutState(true /* aborted */);
-            ImeTracker.forLogging().onFailed(
-                    mImeRequesterStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
-            mImeRequesterStatsToken = null;
+        if (mImeRequester == null) {
+            return;
         }
-        mImeRequester = null;
-        mIsImeLayoutDrawn = false;
-        mShowImeRunner = null;
+        ProtoLog.d(WM_DEBUG_IME, "abortShowImePostLayout");
+        Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
+        logIsScheduledAndReadyToShowIme(true /* aborted */);
+        ImeTracker.forLogging().onFailed(
+                mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
+        resetShowImePostLayout();
     }
 
+    /** Resets the state of the previously scheduled show IME request. */
+    private void resetShowImePostLayout() {
+        mImeRequester = null;
+        mStatsToken = null;
+    }
+
+    /** Checks whether there is a previously scheduled show IME request and we are ready to show. */
     @VisibleForTesting
-    boolean isReadyToShowIme() {
+    boolean isScheduledAndReadyToShowIme() {
         // IMMS#mLastImeTargetWindow always considers focused window as
         // IME target, however DisplayContent#computeImeTarget() can compute
         // a different IME target.
@@ -334,32 +322,47 @@
         // Also, if imeTarget is closing, it would be considered as outdated target.
         // TODO(b/139861270): Remove the child & sublayer check once IMMS is aware of
         //  actual IME target.
+        if (mImeRequester == null) {
+            // No show IME request previously scheduled.
+            return false;
+        }
+        if (!mServerVisible || mFrozen) {
+            // The window container is not available and considered visible.
+            // If frozen, the server visibility is not set until unfrozen.
+            return false;
+        }
+        if (mWindowContainer == null) {
+            // No window container set.
+            return false;
+        }
+        final WindowState windowState = mWindowContainer.asWindowState();
+        if (windowState == null) {
+            throw new IllegalArgumentException("IME insets must be provided by a window.");
+        }
+        if (!windowState.isDrawn() || windowState.mGivenInsetsPending) {
+            // The window is not drawn, or it has pending insets.
+            return false;
+        }
         final InsetsControlTarget dcTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
-        if (dcTarget == null || mImeRequester == null) {
-            // Not ready to show if there is no IME layering target, or no IME requester.
+        if (dcTarget == null) {
+            // No IME layering target.
             return false;
         }
         final InsetsControlTarget controlTarget = getControlTarget();
         if (controlTarget == null) {
-            // Not ready to show if there is no IME control target.
+            // No IME control target.
             return false;
         }
         if (controlTarget != mDisplayContent.getImeTarget(IME_TARGET_CONTROL)) {
-            // Not ready to show if control target does not match the one in DisplayContent.
-            return false;
-        }
-        if (!mServerVisible || mFrozen) {
-            // Not ready to show if the window container is not available and considered visible.
-            // If frozen, the server visibility is not set until unfrozen.
+            // The control target does not match the one in DisplayContent.
             return false;
         }
         if (mStateController.hasPendingControls(controlTarget)) {
-            // Not ready to show if control target has pending controls.
+            // The control target has pending controls.
             return false;
         }
         if (getLeash(controlTarget) == null) {
-            // Not ready to show if control target has no source control leash (or leash is not
-            // ready for dispatching).
+            // The control target has no source control leash (or it is not ready for dispatching).
             return false;
         }
 
@@ -371,51 +374,44 @@
                 || isAboveImeLayeringTarget(mImeRequester, dcTarget)
                 || isImeFallbackTarget(mImeRequester)
                 || isImeInputTarget(mImeRequester)
-                || sameAsImeControlTarget();
+                || sameAsImeControlTarget(mImeRequester);
     }
 
     /**
-     * Logs the current state required for showImePostLayout to be triggered.
+     * Logs the current state that can be checked by {@link #isScheduledAndReadyToShowIme}.
      *
-     * @param aborted whether the showImePostLayout was aborted or cancelled.
+     * @param aborted whether the scheduled show IME request was aborted or cancelled.
      */
-    private void logShowImePostLayoutState(boolean aborted) {
+    private void logIsScheduledAndReadyToShowIme(boolean aborted) {
         final var windowState = mWindowContainer != null ? mWindowContainer.asWindowState() : null;
         final var dcTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
         final var controlTarget = getControlTarget();
         final var sb = new StringBuilder();
         sb.append("showImePostLayout ").append(aborted ? "aborted" : "cancelled");
-        sb.append(", mWindowContainer is: ");
-        sb.append(mWindowContainer != null ? "non-null" : "null");
+        sb.append(", isScheduledAndReadyToShowIme: ").append(isScheduledAndReadyToShowIme());
+        sb.append(", mImeRequester: ").append(mImeRequester);
+        sb.append(", serverVisible: ").append(mServerVisible);
+        sb.append(", frozen: ").append(mFrozen);
+        sb.append(", mWindowContainer is: ").append(mWindowContainer != null ? "non-null" : "null");
         sb.append(", windowState: ").append(windowState);
         if (windowState != null) {
-            sb.append(", windowState.isDrawn(): ");
-            sb.append(windowState.isDrawn());
-            sb.append(", windowState.mGivenInsetsPending: ");
-            sb.append(windowState.mGivenInsetsPending);
+            sb.append(", isDrawn: ").append(windowState.isDrawn());
+            sb.append(", mGivenInsetsPending: ").append(windowState.mGivenInsetsPending);
         }
-        sb.append(", mIsImeLayoutDrawn: ").append(mIsImeLayoutDrawn);
-        sb.append(", mShowImeRunner: ").append(mShowImeRunner);
-        sb.append(", mImeRequester: ").append(mImeRequester);
         sb.append(", dcTarget: ").append(dcTarget);
         sb.append(", controlTarget: ").append(controlTarget);
-        sb.append("\n");
-        sb.append("isReadyToShowIme(): ").append(isReadyToShowIme());
         if (mImeRequester != null && dcTarget != null && controlTarget != null) {
-            sb.append(", controlTarget == DisplayContent.controlTarget: ");
+            sb.append("\n");
+            sb.append("controlTarget == DisplayContent.controlTarget: ");
             sb.append(controlTarget == mDisplayContent.getImeTarget(IME_TARGET_CONTROL));
             sb.append(", hasPendingControls: ");
             sb.append(mStateController.hasPendingControls(controlTarget));
-            sb.append(", serverVisible: ");
-            sb.append(mServerVisible);
-            sb.append(", frozen: ");
-            sb.append(mFrozen);
-            sb.append(", leash is: ");
-            sb.append(getLeash(controlTarget) != null ? "non-null" : "null");
-            sb.append(", control is: ");
-            sb.append(mControl != null ? "non-null" : "null");
-            sb.append(", mIsLeashReadyForDispatching: ");
-            sb.append(mIsLeashReadyForDispatching);
+            final boolean hasLeash = getLeash(controlTarget) != null;
+            sb.append(", leash is: ").append(hasLeash ? "non-null" : "null");
+            if (!hasLeash) {
+                sb.append(", control is: ").append(mControl != null ? "non-null" : "null");
+                sb.append(", mIsLeashReadyForDispatching: ").append(mIsLeashReadyForDispatching);
+            }
             sb.append(", isImeLayeringTarget: ");
             sb.append(isImeLayeringTarget(mImeRequester, dcTarget));
             sb.append(", isAboveImeLayeringTarget: ");
@@ -425,7 +421,7 @@
             sb.append(", isImeInputTarget: ");
             sb.append(isImeInputTarget(mImeRequester));
             sb.append(", sameAsImeControlTarget: ");
-            sb.append(sameAsImeControlTarget());
+            sb.append(sameAsImeControlTarget(mImeRequester));
         }
         Slog.d(TAG, sb.toString());
     }
@@ -445,19 +441,18 @@
                 && dcTarget.getWindow().mSubLayer > target.getWindow().mSubLayer;
     }
 
-    private boolean isImeFallbackTarget(InsetsControlTarget target) {
+    private boolean isImeFallbackTarget(@NonNull InsetsControlTarget target) {
         return target == mDisplayContent.getImeFallback();
     }
 
-    private boolean isImeInputTarget(InsetsControlTarget target) {
+    private boolean isImeInputTarget(@NonNull InsetsControlTarget target) {
         return target == mDisplayContent.getImeInputTarget();
     }
 
-    private boolean sameAsImeControlTarget() {
-        final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_CONTROL);
-        return target == mImeRequester
-                && (mImeRequester.getWindow() == null
-                || !isImeTargetWindowClosing(mImeRequester.getWindow()));
+    private boolean sameAsImeControlTarget(@NonNull InsetsControlTarget target) {
+        final InsetsControlTarget controlTarget = getControlTarget();
+        return controlTarget == target
+                && (target.getWindow() == null || !isImeTargetWindowClosing(target.getWindow()));
     }
 
     private static boolean isImeTargetWindowClosing(@NonNull WindowState win) {
@@ -467,16 +462,15 @@
                     || win.mActivityRecord.willCloseOrEnterPip());
     }
 
-    private boolean isTargetChangedWithinActivity(InsetsControlTarget target) {
+    private boolean isTargetChangedWithinActivity(@NonNull InsetsControlTarget target) {
         // We don't consider the target out of the activity.
-        if (target == null || target.getWindow() == null) {
+        if (target.getWindow() == null) {
             return false;
         }
         return mImeRequester != target
-                && mImeRequester != null && mShowImeRunner != null
+                && mImeRequester != null
                 && mImeRequester.getWindow() != null
-                && mImeRequester.getWindow().mActivityRecord
-                == target.getWindow().mActivityRecord;
+                && mImeRequester.getWindow().mActivityRecord == target.getWindow().mActivityRecord;
     }
     // ---------------------------------------------------------------------------------------
 
@@ -509,7 +503,6 @@
         if (imeRequesterWindow != null) {
             imeRequesterWindow.dumpDebug(proto, IME_TARGET_FROM_IME, logLevel);
         }
-        proto.write(IS_IME_LAYOUT_DRAWN, mIsImeLayoutDrawn);
         proto.end(token);
     }
 
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 4400ed2..2288fe9 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -198,7 +198,7 @@
             if (mControllable) {
                 mWindowContainer.setControllableInsetProvider(this);
                 if (mPendingControlTarget != mControlTarget) {
-                    updateControlForTarget(mPendingControlTarget, true /* force */);
+                    mStateController.notifyControlTargetChanged(mPendingControlTarget, this);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index dfee164..7a1f57b 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -389,7 +389,7 @@
             newControlTargets.clear();
             // Check for and try to run the scheduled show IME request (if it exists), as we
             // now applied the surface transaction and notified the target of the new control.
-            getImeSourceProvider().checkShowImePostLayout();
+            getImeSourceProvider().checkAndStartShowImePostLayout();
         });
     }
 
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 9e16b8a..16d7b4f 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -260,7 +260,7 @@
     // Whether activity "refresh" was requested but not finished in
     // ActivityRecord#activityResumedLocked following the camera compat force rotation in
     // DisplayRotationCompatPolicy.
-    private boolean mIsRefreshAfterRotationRequested;
+    private boolean mIsRefreshRequested;
 
     @NonNull
     private final OptProp mIgnoreRequestedOrientationOptProp;
@@ -571,15 +571,14 @@
     }
 
     /**
-     * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}
-     * following the camera compat force rotation in {@link DisplayRotationCompatPolicy}.
+     * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}.
      */
-    boolean isRefreshAfterRotationRequested() {
-        return mIsRefreshAfterRotationRequested;
+    boolean isRefreshRequested() {
+        return mIsRefreshRequested;
     }
 
-    void setIsRefreshAfterRotationRequested(boolean isRequested) {
-        mIsRefreshAfterRotationRequested = isRequested;
+    void setIsRefreshRequested(boolean isRequested) {
+        mIsRefreshRequested = isRequested;
     }
 
     boolean isOverrideRespectRequestedOrientationEnabled() {
@@ -989,6 +988,10 @@
         }
     }
 
+    boolean isLetterboxEducationEnabled() {
+        return mLetterboxConfiguration.getIsEducationEnabled();
+    }
+
     /**
      * Whether we use split screen aspect ratio for the activity when camera compat treatment
      * is active because the corresponding config is enabled and activity supports resizing.
@@ -1064,7 +1067,7 @@
      * thin letteboxing
      */
     boolean allowVerticalReachabilityForThinLetterbox() {
-        if (!Flags.disableThinLetterboxingReachability()) {
+        if (!Flags.disableThinLetterboxingPolicy()) {
             return true;
         }
         // When the flag is enabled we allow vertical reachability only if the
@@ -1077,7 +1080,7 @@
      * thin letteboxing
      */
     boolean allowHorizontalReachabilityForThinLetterbox() {
-        if (!Flags.disableThinLetterboxingReachability()) {
+        if (!Flags.disableThinLetterboxingPolicy()) {
             return true;
         }
         // When the flag is enabled we allow horizontal reachability only if the
diff --git a/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java b/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
index 498182d..3606a34 100644
--- a/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
+++ b/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
@@ -41,8 +41,12 @@
 
     PerfettoTransitionTracer() {
         Producer.init(InitArguments.DEFAULTS);
-        mDataSource.register(
-                new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+        DataSourceParams params =
+                new DataSourceParams.Builder()
+                        .setBufferExhaustedPolicy(
+                                PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+                        .build();
+        mDataSource.register(params);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 6dec712..72f592b 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -64,7 +64,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArraySet;
diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java
index 8680436..1e6ee7d 100644
--- a/services/core/java/com/android/server/wm/SnapshotCache.java
+++ b/services/core/java/com/android/server/wm/SnapshotCache.java
@@ -92,6 +92,7 @@
             if (entry != null) {
                 mAppIdMap.remove(entry.topApp);
                 mRunningCache.remove(id);
+                entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index 3578971..42ca7b4 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -253,6 +253,7 @@
                 PersistInfoProvider provider) {
             super(provider, userId);
             mId = id;
+            snapshot.addReference(TaskSnapshot.REFERENCE_PERSIST);
             mSnapshot = snapshot;
         }
 
@@ -289,6 +290,7 @@
             if (failed) {
                 deleteSnapshot(mId, mUserId, mPersistInfoProvider);
             }
+            mSnapshot.removeReference(TaskSnapshot.REFERENCE_PERSIST);
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8bd7b5f..a555388 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -297,6 +297,10 @@
     ActivityRecord mTranslucentActivityWaiting = null;
     ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = new ArrayList<>();
 
+    // The topmost Activity that was converted to translucent for scene transition, which should
+    // be converted from translucent once the transition is completed, or the app died.
+    private ActivityRecord mPendingConvertFromTranslucentActivity = null;
+
     /**
      * Set when we know we are going to be calling updateConfiguration()
      * soon, so want to skip intermediate config checks.
@@ -3448,6 +3452,8 @@
         // Whether the direct top activity is eligible for letterbox education.
         appCompatTaskInfo.topActivityEligibleForLetterboxEducation = isTopActivityResumed
                 && top.isEligibleForLetterboxEducation();
+        appCompatTaskInfo.isLetterboxEducationEnabled = top != null
+                && top.mLetterboxUiController.isLetterboxEducationEnabled();
         // Whether the direct top activity requested showing camera compat control.
         appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState = isTopActivityResumed
                 ? top.getCameraCompatControlState()
@@ -4986,6 +4992,27 @@
         }
     }
 
+    void abortTranslucentActivityWaiting(@NonNull ActivityRecord r) {
+        if (r != mTranslucentActivityWaiting && r != mPendingConvertFromTranslucentActivity) {
+            return;
+        }
+
+        if (mTranslucentActivityWaiting != null) {
+            if (!mTranslucentActivityWaiting.finishing) {
+                mTranslucentActivityWaiting.setOccludesParent(true);
+            }
+            mTranslucentActivityWaiting = null;
+        }
+        if (mPendingConvertFromTranslucentActivity != null) {
+            if (!mPendingConvertFromTranslucentActivity.finishing) {
+                mPendingConvertFromTranslucentActivity.setOccludesParent(true);
+            }
+            mPendingConvertFromTranslucentActivity = null;
+        }
+        mUndrawnActivitiesBelowTopTranslucent.clear();
+        mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+    }
+
     void checkTranslucentActivityWaiting(ActivityRecord top) {
         if (mTranslucentActivityWaiting != top) {
             mUndrawnActivitiesBelowTopTranslucent.clear();
@@ -5000,10 +5027,19 @@
 
     void convertActivityToTranslucent(ActivityRecord r) {
         mTranslucentActivityWaiting = r;
+        mPendingConvertFromTranslucentActivity = r;
         mUndrawnActivitiesBelowTopTranslucent.clear();
         mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
     }
 
+    void convertActivityFromTranslucent(ActivityRecord r) {
+        if (r != mPendingConvertFromTranslucentActivity) {
+            Slog.e(TAG, "convertFromTranslucent expects " + mPendingConvertFromTranslucentActivity
+                    + " but is " + r);
+        }
+        mPendingConvertFromTranslucentActivity = null;
+    }
+
     /**
      * Called as activities below the top translucent activity are redrawn. When the last one is
      * redrawn notify the top activity by calling
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 21e7a8d..586f3c3 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -247,6 +247,7 @@
                     break;
                 case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG:
                     forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg);
+                    ((TaskSnapshot) msg.obj).removeReference(TaskSnapshot.REFERENCE_BROADCAST);
                     break;
                 case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
                     forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
@@ -485,6 +486,7 @@
      * Notify listeners that the snapshot of a task has changed.
      */
     void notifyTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+        snapshot.addReference(TaskSnapshot.REFERENCE_BROADCAST);
         final Message msg = mHandler.obtainMessage(NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG,
                 taskId, 0, snapshot);
         forAllLocalListeners(mNotifyTaskSnapshotChanged, msg);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 2c27b98..eff8315 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -223,7 +223,7 @@
 
     @VisibleForTesting
     Task getTopRootTask() {
-        return getRootTask(t -> true);
+        return getRootTask(alwaysTruePredicate());
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 6a7f60b..26e4eaa 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -40,6 +40,8 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.os.UserHandle.USER_NULL;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
@@ -354,14 +356,21 @@
 
     /**
      * Whether the activity navigation should be isolated. That is, Activities cannot be launched
-     * on an isolated TaskFragment, unless the activity is launched from an Activity in the same
-     * isolated TaskFragment, or explicitly requested to be launched to.
-     * <p>
-     * Note that only an embedded TaskFragment can be isolated.
+     * on an isolated TaskFragment unless explicitly requested to be launched to.
      */
     private boolean mIsolatedNav;
 
     /**
+     * Whether the TaskFragment to be pinned.
+     * <p>
+     * If a TaskFragment is pinned, the TaskFragment should be the top-most TaskFragment among other
+     * sibling TaskFragments. Any newly launched and embeddable activity should not be placed in the
+     * pinned TaskFragment, unless the activity is launched from the pinned TaskFragment or
+     * explicitly requested to. Non-embeddable activities are not restricted to.
+     */
+    private boolean mPinned;
+
+    /**
      * Whether the TaskFragment should move to bottom of task when any activity below it is
      * launched in clear top mode.
      */
@@ -515,6 +524,18 @@
     }
 
     /**
+     * Sets whether this TaskFragment {@link #isPinned()}.
+     * <p>
+     * Note that this is no-op if the TaskFragment is not {@link #isEmbedded() embedded}.
+     */
+    void setPinned(boolean pinned) {
+        if (!isEmbedded()) {
+            return;
+        }
+        mPinned = pinned;
+    }
+
+    /**
      * Sets whether transitions are allowed when the TaskFragment is empty. If {@code true},
      * transitions are allowed when the TaskFragment is empty. If {@code false}, transitions
      * will wait until the TaskFragment becomes non-empty or other conditions are met. Default
@@ -532,6 +553,15 @@
         return isEmbedded() && mIsolatedNav;
     }
 
+    /**
+     * Indicates whether this TaskFragment is pinned.
+     *
+     * @see android.window.TaskFragmentOperation#OP_TYPE_SET_PINNED
+     */
+    boolean isPinned() {
+        return isEmbedded() && mPinned;
+    }
+
     TaskFragment getAdjacentTaskFragment() {
         return mAdjacentTaskFragment;
     }
@@ -564,7 +594,6 @@
     }
 
     void setResumedActivity(ActivityRecord r, String reason) {
-        warnForNonLeafTaskFragment("setResumedActivity");
         if (mResumedActivity == r) {
             return;
         }
@@ -850,15 +879,6 @@
         return parentTaskFragment != null ? parentTaskFragment.getOrganizedTaskFragment() : null;
     }
 
-    /**
-     * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}.
-     */
-    private void warnForNonLeafTaskFragment(String func) {
-        if (!isLeafTaskFragment()) {
-            Slog.w(TAG, func + " on non-leaf task fragment " + this);
-        }
-    }
-
     boolean hasDirectChildActivities() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             if (mChildren.get(i).asActivityRecord() != null) {
@@ -935,7 +955,6 @@
      */
     void onActivityStateChanged(ActivityRecord record, ActivityRecord.State state,
             String reason) {
-        warnForNonLeafTaskFragment("onActivityStateChanged");
         if (record == mResumedActivity && state != RESUMED) {
             setResumedActivity(null, reason + " - onActivityStateChanged");
         }
@@ -965,7 +984,6 @@
      * @return {@code true} if the process of the pausing activity is died.
      */
     boolean handleAppDied(WindowProcessController app) {
-        warnForNonLeafTaskFragment("handleAppDied");
         boolean isPausingDied = false;
         if (mPausingActivity != null && mPausingActivity.app == app) {
             ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s",
@@ -2206,7 +2224,7 @@
     static class ConfigOverrideHint {
         @Nullable DisplayInfo mTmpOverrideDisplayInfo;
         @Nullable ActivityRecord.CompatDisplayInsets mTmpCompatInsets;
-        boolean mUseOverrideInsetsForStableBounds;
+        boolean mUseOverrideInsetsForConfig;
     }
 
     void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@@ -2239,11 +2257,11 @@
             @NonNull Configuration parentConfig, @Nullable ConfigOverrideHint overrideHint) {
         DisplayInfo overrideDisplayInfo = null;
         ActivityRecord.CompatDisplayInsets compatInsets = null;
-        boolean useOverrideInsetsForStableBounds = false;
+        boolean useOverrideInsetsForConfig = false;
         if (overrideHint != null) {
             overrideDisplayInfo = overrideHint.mTmpOverrideDisplayInfo;
             compatInsets = overrideHint.mTmpCompatInsets;
-            useOverrideInsetsForStableBounds = overrideHint.mUseOverrideInsetsForStableBounds;
+            useOverrideInsetsForConfig = overrideHint.mUseOverrideInsetsForConfig;
             if (overrideDisplayInfo != null) {
                 // Make sure the screen related configs can be computed by the provided
                 // display info.
@@ -2307,6 +2325,7 @@
             }
         }
 
+        boolean insetsOverrideApplied = false;
         if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
                 || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
             if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
@@ -2323,7 +2342,7 @@
                 // The non decor inset are areas that could never be removed in Honeycomb. See
                 // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
                 calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di,
-                        useOverrideInsetsForStableBounds);
+                        useOverrideInsetsForConfig);
             } else {
                 // Apply the given non-decor and stable insets to calculate the corresponding bounds
                 // for screen size of configuration.
@@ -2340,8 +2359,21 @@
                     intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
                             compatInsets.mStableInsets[rotation]);
                     outAppBounds.set(mTmpNonDecorBounds);
+                } else if (useOverrideInsetsForConfig) {
+                    final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+                    final int dw = rotated ? mDisplayContent.mBaseDisplayHeight
+                            : mDisplayContent.mBaseDisplayWidth;
+                    final int dh = rotated ? mDisplayContent.mBaseDisplayWidth
+                            : mDisplayContent.mBaseDisplayHeight;
+                    final DisplayPolicy.DecorInsets.Info decorInsets = mDisplayContent
+                            .getDisplayPolicy().getDecorInsetsInfo(rotation, dw, dh);
+                    mTmpStableBounds.set(outAppBounds);
+                    mTmpStableBounds.inset(decorInsets.mOverrideConfigInsets);
+                    outAppBounds.inset(decorInsets.mOverrideNonDecorInsets);
+                    mTmpNonDecorBounds.set(outAppBounds);
+                    // Record the override apply to avoid duplicated check.
+                    insetsOverrideApplied = true;
                 } else {
-                    // Set to app bounds because it excludes decor insets.
                     mTmpNonDecorBounds.set(outAppBounds);
                     mTmpStableBounds.set(outAppBounds);
                 }
@@ -2383,6 +2415,11 @@
                     // from the parent task would result in applications loaded wrong resource.
                     inOutConfig.smallestScreenWidthDp =
                             Math.min(inOutConfig.screenWidthDp, inOutConfig.screenHeightDp);
+                } else if (insetsOverrideApplied) {
+                    // The smallest width should also consider insets. If the insets are overridden,
+                    // use the overridden value.
+                    inOutConfig.smallestScreenWidthDp =
+                            Math.min(inOutConfig.screenWidthDp, inOutConfig.screenHeightDp);
                 }
                 // otherwise, it will just inherit
             }
@@ -2895,6 +2932,13 @@
         return !mCreatedByOrganizer || mIsRemovalRequested;
     }
 
+    /**
+     * Returns whether this TaskFragment is going to be removed.
+     */
+    boolean isRemovalRequested() {
+        return mIsRemovalRequested;
+    }
+
     @Override
     void removeChild(WindowContainer child) {
         removeChild(child, true /* removeSelfIfPossible */);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index b69ac1b..64b9df5 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -35,9 +35,11 @@
 
     void putSnapshot(Task task, TaskSnapshot snapshot) {
         synchronized (mLock) {
+            snapshot.addReference(TaskSnapshot.REFERENCE_CACHE);
             final CacheEntry entry = mRunningCache.get(task.mTaskId);
             if (entry != null) {
                 mAppIdMap.remove(entry.topApp);
+                entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE);
             }
             final ActivityRecord top = task.getTopMostActivity();
             mAppIdMap.put(top, task.mTaskId);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d70ca02..edbba92 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -116,6 +116,7 @@
 import com.android.server.wm.SurfaceAnimator.Animatable;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import com.android.server.wm.utils.AlwaysTruePredicate;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -2019,29 +2020,34 @@
                 callback, boundary, includeBoundary, traverseTopToBottom, boundaryFound);
     }
 
+    @SuppressWarnings("unchecked")
+    static <T> Predicate<T> alwaysTruePredicate() {
+        return (Predicate<T>) AlwaysTruePredicate.INSTANCE;
+    }
+
     ActivityRecord getActivityAbove(ActivityRecord r) {
-        return getActivity((above) -> true, r,
+        return getActivity(alwaysTruePredicate(), r /* boundary */,
                 false /*includeBoundary*/, false /*traverseTopToBottom*/);
     }
 
     ActivityRecord getActivityBelow(ActivityRecord r) {
-        return getActivity((below) -> true, r,
+        return getActivity(alwaysTruePredicate(), r /* boundary */,
                 false /*includeBoundary*/, true /*traverseTopToBottom*/);
     }
 
     ActivityRecord getBottomMostActivity() {
-        return getActivity((r) -> true, false /*traverseTopToBottom*/);
+        return getActivity(alwaysTruePredicate(), false /* traverseTopToBottom */);
     }
 
     ActivityRecord getTopMostActivity() {
-        return getActivity((r) -> true, true /*traverseTopToBottom*/);
+        return getActivity(alwaysTruePredicate(), true /* traverseTopToBottom */);
     }
 
     ActivityRecord getTopActivity(boolean includeFinishing, boolean includeOverlays) {
         // Break down into 4 calls to avoid object creation due to capturing input params.
         if (includeFinishing) {
             if (includeOverlays) {
-                return getActivity((r) -> true);
+                return getActivity(alwaysTruePredicate());
             }
             return getActivity((r) -> !r.isTaskOverlay());
         } else if (includeOverlays) {
@@ -2220,21 +2226,17 @@
         }
     }
 
-    Task getTaskAbove(Task t) {
-        return getTask(
-                (above) -> true, t, false /*includeBoundary*/, false /*traverseTopToBottom*/);
-    }
-
     Task getTaskBelow(Task t) {
-        return getTask((below) -> true, t, false /*includeBoundary*/, true /*traverseTopToBottom*/);
+        return getTask(alwaysTruePredicate(), t /* boundary */,
+                false /* includeBoundary */, true /* traverseTopToBottom */);
     }
 
     Task getBottomMostTask() {
-        return getTask((t) -> true, false /*traverseTopToBottom*/);
+        return getTask(alwaysTruePredicate(), false /* traverseTopToBottom */);
     }
 
     Task getTopMostTask() {
-        return getTask((t) -> true, true /*traverseTopToBottom*/);
+        return getTask(alwaysTruePredicate(), true /* traverseTopToBottom */);
     }
 
     Task getTask(Predicate<Task> callback) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e02e5be..b603551 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8302,7 +8302,6 @@
                 ImeTracker.forLogging().onProgress(statsToken,
                         ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET);
 
-                Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
                 final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget();
                 imeTarget = controlTarget.getWindow();
                 // If InsetsControlTarget doesn't have a window, it's using remoteControlTarget
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 90e7bd7..99c4736 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -39,6 +39,7 @@
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS;
 import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN;
@@ -1627,6 +1628,11 @@
                 }
                 break;
             }
+            case OP_TYPE_SET_PINNED: {
+                final boolean pinned = operation.getBooleanValue();
+                taskFragment.setPinned(pinned);
+                break;
+            }
         }
         return effects;
     }
diff --git a/services/core/java/com/android/server/wm/utils/AlwaysTruePredicate.java b/services/core/java/com/android/server/wm/utils/AlwaysTruePredicate.java
new file mode 100644
index 0000000..49dcb6c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/AlwaysTruePredicate.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import java.util.function.Predicate;
+
+/** A simple Predicate to avoid synthetic allocation of lambda expression "o -> true". */
+public class AlwaysTruePredicate implements Predicate<Object> {
+
+    public static final AlwaysTruePredicate INSTANCE = new AlwaysTruePredicate();
+
+    private AlwaysTruePredicate() {
+    }
+
+    @Override
+    public boolean test(Object o) {
+        return true;
+    }
+}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a01c123..97f1e19 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -122,7 +122,6 @@
     jmethodID interceptMotionBeforeQueueingNonInteractive;
     jmethodID interceptKeyBeforeDispatching;
     jmethodID dispatchUnhandledKey;
-    jmethodID onPointerDisplayIdChanged;
     jmethodID onPointerDownOutsideFocus;
     jmethodID getVirtualKeyQuietTimeMillis;
     jmethodID getExcludedDeviceNames;
@@ -423,7 +422,7 @@
         std::set<int32_t> disabledInputDevices{};
 
         // Associated Pointer controller display.
-        ui::LogicalDisplayId pointerDisplayId{ui::ADISPLAY_ID_DEFAULT};
+        ui::LogicalDisplayId pointerDisplayId{ui::LogicalDisplayId::DEFAULT};
 
         // True if stylus button reporting through motion events is enabled.
         bool stylusButtonMotionEventsEnabled{true};
@@ -786,12 +785,6 @@
     } // release lock
     mInputManager->getReader().requestRefreshConfiguration(
             InputReaderConfiguration::Change::DISPLAY_INFO);
-
-    // Notify the system.
-    JNIEnv* env = jniEnv();
-    env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDisplayIdChanged, pointerDisplayId,
-                        position.x, position.y);
-    checkAndClearExceptionFromCallback(env, "onPointerDisplayIdChanged");
 }
 
 void NativeInputManager::notifyStickyModifierStateChanged(uint32_t modifierState,
@@ -1886,7 +1879,7 @@
                                         jstring nameObj, jint pid) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
 
-    if (displayId == ui::ADISPLAY_ID_NONE.val()) {
+    if (ui::LogicalDisplayId{displayId} == ui::LogicalDisplayId::INVALID) {
         std::string message = "InputChannel used as a monitor must be associated with a display";
         jniThrowRuntimeException(env, message.c_str());
         return nullptr;
@@ -2727,6 +2720,11 @@
     im->setInputMethodConnectionIsActive(isActive);
 }
 
+static jint nativeGetLastUsedInputDeviceId(JNIEnv* env, jobject nativeImplObj) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+    return static_cast<jint>(im->getInputManager()->getReader().getLastUsedInputDeviceId());
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gInputManagerMethods[] = {
@@ -2835,6 +2833,7 @@
         {"setAccessibilityStickyKeysEnabled", "(Z)V",
          (void*)nativeSetAccessibilityStickyKeysEnabled},
         {"setInputMethodConnectionIsActive", "(Z)V", (void*)nativeSetInputMethodConnectionIsActive},
+        {"getLastUsedInputDeviceId", "()I", (void*)nativeGetLastUsedInputDeviceId},
 };
 
 #define FIND_CLASS(var, className) \
@@ -2927,9 +2926,6 @@
             "dispatchUnhandledKey",
             "(Landroid/os/IBinder;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
 
-    GET_METHOD_ID(gServiceClassInfo.onPointerDisplayIdChanged, clazz, "onPointerDisplayIdChanged",
-                  "(IFF)V");
-
     GET_METHOD_ID(gServiceClassInfo.notifyStickyModifierStateChanged, clazz,
                   "notifyStickyModifierStateChanged", "(II)V");
 
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 6143f1d..610b502 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -746,6 +746,20 @@
                     minOccurs="0" maxOccurs="1">
             <xs:annotation name="final"/>
         </xs:element>
+        <!-- list of supported modes for low power. Each point corresponds to one mode.
+          Mode format is : first = refreshRate, second = vsyncRate. E.g. :
+          <lowPowerSupportedModes>
+              <point>
+                  <first>60</first>   // refreshRate
+                  <second>60</second> //vsyncRate
+              </point>
+              ....
+          </lowPowerSupportedModes>
+           -->
+        <xs:element type="nonNegativeFloatToFloatMap" name="lowPowerSupportedModes" minOccurs="0">
+            <xs:annotation name="nullable"/>
+            <xs:annotation name="final"/>
+        </xs:element>
     </xs:complexType>
 
     <xs:complexType name="refreshRateZoneProfiles">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 45ec8f2..203a6d9 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -360,6 +360,7 @@
     method public final java.math.BigInteger getDefaultRefreshRateInHbmHdr();
     method public final java.math.BigInteger getDefaultRefreshRateInHbmSunlight();
     method public final com.android.server.display.config.BlockingZoneConfig getHigherBlockingZoneConfigs();
+    method @Nullable public final com.android.server.display.config.NonNegativeFloatToFloatMap getLowPowerSupportedModes();
     method public final com.android.server.display.config.BlockingZoneConfig getLowerBlockingZoneConfigs();
     method public final com.android.server.display.config.RefreshRateZoneProfiles getRefreshRateZoneProfiles();
     method public final void setDefaultPeakRefreshRate(java.math.BigInteger);
@@ -367,6 +368,7 @@
     method public final void setDefaultRefreshRateInHbmHdr(java.math.BigInteger);
     method public final void setDefaultRefreshRateInHbmSunlight(java.math.BigInteger);
     method public final void setHigherBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig);
+    method public final void setLowPowerSupportedModes(@Nullable com.android.server.display.config.NonNegativeFloatToFloatMap);
     method public final void setLowerBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig);
     method public final void setRefreshRateZoneProfiles(com.android.server.display.config.RefreshRateZoneProfiles);
   }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index d114337..d733762 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -217,7 +217,7 @@
     <V> void setLocalPolicy(
             @NonNull PolicyDefinition<V> policyDefinition,
             @NonNull EnforcingAdmin enforcingAdmin,
-            @Nullable PolicyValue<V> value,
+            @NonNull PolicyValue<V> value,
             int userId,
             boolean skipEnforcePolicy) {
         Objects.requireNonNull(policyDefinition);
@@ -313,6 +313,7 @@
         }
         updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin);
         write();
+        applyToInheritableProfiles(policyDefinition, enforcingAdmin, value, userId);
     }
 
     // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
@@ -400,7 +401,7 @@
      * else remove the policy from child.
      */
     private <V> void applyToInheritableProfiles(PolicyDefinition<V> policyDefinition,
-            EnforcingAdmin enforcingAdmin, PolicyValue<V> value, int userId) {
+            EnforcingAdmin enforcingAdmin, @Nullable PolicyValue<V> value, int userId) {
         if (policyDefinition.isInheritable()) {
             Binder.withCleanCallingIdentity(() -> {
                 List<UserInfo> userInfos = mUserManager.getProfiles(userId);
@@ -1742,14 +1743,17 @@
         }
     }
 
-    <V> void reapplyAllPoliciesLocked() {
+    <V> void reapplyAllPoliciesOnBootLocked() {
         for (PolicyKey policy : mGlobalPolicies.keySet()) {
             PolicyState<?> policyState = mGlobalPolicies.get(policy);
             // Policy definition and value will always be of the same type
             PolicyDefinition<V> policyDefinition =
                     (PolicyDefinition<V>) policyState.getPolicyDefinition();
-            PolicyValue<V> policyValue = (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
-            enforcePolicy(policyDefinition, policyValue, UserHandle.USER_ALL);
+            if (!policyDefinition.shouldSkipEnforcementIfNotChanged()) {
+                PolicyValue<V> policyValue =
+                        (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
+                enforcePolicy(policyDefinition, policyValue, UserHandle.USER_ALL);
+            }
         }
         for (int i = 0; i < mLocalPolicies.size(); i++) {
             int userId = mLocalPolicies.keyAt(i);
@@ -1758,10 +1762,11 @@
                 // Policy definition and value will always be of the same type
                 PolicyDefinition<V> policyDefinition =
                         (PolicyDefinition<V>) policyState.getPolicyDefinition();
-                PolicyValue<V> policyValue =
-                        (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
-                enforcePolicy(policyDefinition, policyValue, userId);
-
+                if (!policyDefinition.shouldSkipEnforcementIfNotChanged()) {
+                    PolicyValue<V> policyValue =
+                            (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
+                    enforcePolicy(policyDefinition, policyValue, userId);
+                }
             }
         }
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 375fc5a..85d2a0d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -339,6 +339,7 @@
 import android.app.admin.ManagedSubscriptionsPolicy;
 import android.app.admin.NetworkEvent;
 import android.app.admin.PackagePolicy;
+import android.app.admin.PackageSetPolicyValue;
 import android.app.admin.ParcelableGranteeMap;
 import android.app.admin.ParcelableResource;
 import android.app.admin.PasswordMetrics;
@@ -349,7 +350,6 @@
 import android.app.admin.PreferentialNetworkServiceConfig;
 import android.app.admin.SecurityLog;
 import android.app.admin.SecurityLog.SecurityEvent;
-import android.app.admin.PackageSetPolicyValue;
 import android.app.admin.StartInstallingUpdateCallback;
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
@@ -2718,6 +2718,7 @@
                     mDevicePolicyEngine.getResolvedPolicy(
                             PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL));
             setLoggingConfiguration(securityLoggingEnabled, auditLoggingEnabled);
+            mInjector.runCryptoSelfTest();
         } else {
             synchronized (getLockObject()) {
                 mSecurityLogMonitor.start(getSecurityLoggingEnabledUser());
@@ -3350,7 +3351,7 @@
                 break;
             case SystemService.PHASE_SYSTEM_SERVICES_READY:
                 synchronized (getLockObject()) {
-                    mDevicePolicyEngine.reapplyAllPoliciesLocked();
+                    mDevicePolicyEngine.reapplyAllPoliciesOnBootLocked();
                 }
                 break;
             case SystemService.PHASE_ACTIVITY_MANAGER_READY:
@@ -11442,7 +11443,7 @@
             }
             setBackwardsCompatibleAppRestrictions(
                     caller, packageName, restrictions, caller.getUserHandle());
-        } else if (Flags.dmrhCanSetAppRestriction()) {
+        } else if (Flags.dmrhSetAppRestrictions()) {
             final boolean isRoleHolder;
             if (who != null) {
                 // DO or PO
@@ -11483,10 +11484,6 @@
                             new BundlePolicyValue(restrictions),
                             affectedUserId);
                 }
-                Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
-                changeIntent.setPackage(packageName);
-                changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                mContext.sendBroadcastAsUser(changeIntent, UserHandle.of(affectedUserId));
             } else {
                 mInjector.binderWithCleanCallingIdentity(() -> {
                     mUserManager.setApplicationRestrictions(packageName, restrictions,
@@ -12844,7 +12841,7 @@
                 return Bundle.EMPTY;
             }
             return policies.get(enforcingAdmin).getValue();
-        } else if (Flags.dmrhCanSetAppRestriction()) {
+        } else if (Flags.dmrhSetAppRestrictions()) {
             final boolean isRoleHolder;
             if (who != null) {
                 // Caller is DO or PO. They cannot call this on parent
@@ -15172,10 +15169,8 @@
             if (statusBarService != null) {
                 int flags1 = disabled ? STATUS_BAR_DISABLE_MASK : StatusBarManager.DISABLE_NONE;
                 int flags2 = disabled ? STATUS_BAR_DISABLE2_MASK : StatusBarManager.DISABLE2_NONE;
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo(flags1,
-                        flags2);
-                statusBarService.disableForUser(info, mToken, mContext.getPackageName(), userId,
-                        "setStatusBarDisabledInternal");
+                statusBarService.disableForUser(flags1, mToken, mContext.getPackageName(), userId);
+                statusBarService.disable2ForUser(flags2, mToken, mContext.getPackageName(), userId);
                 return true;
             }
         } catch (RemoteException e) {
@@ -15771,8 +15766,13 @@
                             PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
                             userId);
             List<Bundle> restrictions = new ArrayList<>();
-            for (EnforcingAdmin admin : policies.keySet()) {
-                restrictions.add(policies.get(admin).getValue());
+            for (PolicyValue<Bundle> policyValue: policies.values()) {
+                Bundle value = policyValue.getValue();
+                // Probably not necessary since setApplicationRestrictions only sets non-empty
+                // Bundle, but just in case.
+                if (value != null && !value.isEmpty()) {
+                    restrictions.add(value);
+                }
             }
 
             return mInjector.binderWithCleanCallingIdentity(() -> {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
index 1000bfa..cbd2847 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
@@ -52,7 +52,18 @@
     EnterpriseSpecificIdCalculator(Context context) {
         TelephonyManager telephonyService = context.getSystemService(TelephonyManager.class);
         Preconditions.checkState(telephonyService != null, "Unable to access telephony service");
-        mImei = telephonyService.getImei(0);
+
+        String imei;
+        try {
+            imei = telephonyService.getImei(0);
+        } catch (UnsupportedOperationException doesNotSupportGms) {
+            // Instead of catching the exception, we could check for FEATURE_TELEPHONY_GSM.
+            // However that runs the risk of changing a device's existing ESID if on these devices
+            // telephonyService.getImei() actually returns non-null even when the device does not
+            // declare FEATURE_TELEPHONY_GSM.
+            imei = null;
+        }
+        mImei = imei;
         String meid;
         try {
             meid = telephonyService.getMeid(0);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 8d980b5..8bec384 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -51,6 +51,7 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 final class PolicyDefinition<V> {
@@ -82,6 +83,10 @@
     // them.
     private static final int POLICY_FLAG_USER_RESTRICTION_POLICY = 1 << 4;
 
+    // Only invoke the policy enforcer callback when the policy value changes, and do not invoke the
+    // callback in other cases such as device reboots.
+    private static final int POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED = 1 << 5;
+
     private static final MostRestrictive<Boolean> FALSE_MORE_RESTRICTIVE = new MostRestrictive<>(
             List.of(new BooleanPolicyValue(false), new BooleanPolicyValue(true)));
 
@@ -231,11 +236,11 @@
                     // Don't need to take in a resolution mechanism since its never used, but might
                     // need some refactoring to not always assume a non-null mechanism.
                     new MostRecent<>(),
-                    POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_NON_COEXISTABLE_POLICY,
-                    // Application restrictions are now stored and retrieved from DPMS, so no
-                    // enforcing is required, however DPMS calls into UM to set restrictions for
-                    // backwards compatibility.
-                    (Bundle value, Context context, Integer userId, PolicyKey policyKey) -> true,
+                    // Only invoke the enforcement callback during policy change and not other state
+                    POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE
+                            | POLICY_FLAG_NON_COEXISTABLE_POLICY
+                            | POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED,
+                    PolicyEnforcerCallbacks::setApplicationRestrictions,
                     new BundlePolicySerializer());
 
     /**
@@ -581,6 +586,10 @@
         return (mPolicyFlags & POLICY_FLAG_USER_RESTRICTION_POLICY) != 0;
     }
 
+    boolean shouldSkipEnforcementIfNotChanged() {
+        return (mPolicyFlags & POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED) != 0;
+    }
+
     @Nullable
     PolicyValue<V> resolvePolicy(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminsPolicy) {
         return mResolutionMechanism.resolve(adminsPolicy);
@@ -610,7 +619,7 @@
      * {@link Object#equals} implementation.
      */
     private PolicyDefinition(
-            PolicyKey key,
+            @NonNull  PolicyKey key,
             ResolutionMechanism<V> resolutionMechanism,
             QuadFunction<V, Context, Integer, PolicyKey, Boolean> policyEnforcerCallback,
             PolicySerializer<V> policySerializer) {
@@ -622,11 +631,12 @@
      * {@link Object#equals} and {@link Object#hashCode()} implementation.
      */
     private PolicyDefinition(
-            PolicyKey policyKey,
+            @NonNull  PolicyKey policyKey,
             ResolutionMechanism<V> resolutionMechanism,
             int policyFlags,
             QuadFunction<V, Context, Integer, PolicyKey, Boolean> policyEnforcerCallback,
             PolicySerializer<V> policySerializer) {
+        Objects.requireNonNull(policyKey);
         mPolicyKey = policyKey;
         mResolutionMechanism = resolutionMechanism;
         mPolicyFlags = policyFlags;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 09eef45..04d277e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -37,11 +37,13 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -172,6 +174,29 @@
         return true;
     }
 
+
+    /**
+     * Application restrictions are stored and retrieved from DPMS, so no enforcing (aka pushing
+     * it to UMS) is required. Only need to send broadcast to the target user here as we rely on
+     * the inheritable policy propagation logic in PolicyEngine to apply this policy to multiple
+     * profiles. The broadcast should only be sent when an application restriction is set, so we
+     * rely on the POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED flag so DPE only invokes this callback
+     * when the policy is set, and not during system boot or other situations.
+     */
+    static boolean setApplicationRestrictions(Bundle bundle, Context context, Integer userId,
+            PolicyKey policyKey) {
+        Binder.withCleanCallingIdentity(() -> {
+            PackagePolicyKey key = (PackagePolicyKey) policyKey;
+            String packageName = key.getPackageName();
+            Objects.requireNonNull(packageName);
+            Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
+            changeIntent.setPackage(packageName);
+            changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            context.sendBroadcastAsUser(changeIntent, UserHandle.of(userId));
+        });
+        return true;
+    }
+
     private static class BlockingCallback {
         private final CountDownLatch mLatch = new CountDownLatch(1);
         private final AtomicReference<Boolean> mValue = new AtomicReference<>();
diff --git a/services/people/java/com/android/server/people/TEST_MAPPING b/services/people/java/com/android/server/people/TEST_MAPPING
new file mode 100644
index 0000000..55b355c
--- /dev/null
+++ b/services/people/java/com/android/server/people/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+    "presubmit": [
+        {
+            "name": "FrameworksServicesTests",
+            "options": [
+                {
+                    "include-filter": "com.android.server.people.data"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 9e4f821..d307200 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -1276,7 +1276,23 @@
                     packageName,
                     permissionName
                 )
-            else -> permissionAllowlist.getSignatureAppAllowlistState(packageName, permissionName)
+            else ->
+                permissionAllowlist.getProductSignatureAppAllowlistState(
+                    packageName,
+                    permissionName
+                )
+                    ?: permissionAllowlist.getVendorSignatureAppAllowlistState(
+                        packageName,
+                        permissionName
+                    )
+                    ?: permissionAllowlist.getSystemExtSignatureAppAllowlistState(
+                        packageName,
+                        permissionName
+                    )
+                    ?: permissionAllowlist.getSignatureAppAllowlistState(
+                        packageName,
+                        permissionName
+                    )
         }
     }
 
diff --git a/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionTest.java
index 6a82f16..3e87c6f 100644
--- a/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -543,6 +544,18 @@
         return future.get();
     }
 
+    @Test
+    public void onBindingDied_referenceLost_doesNotThrow() {
+        TransportConnection.TransportConnectionMonitor transportConnectionMonitor =
+                new TransportConnection.TransportConnectionMonitor(
+                        mContext, /* transportConnection= */ null);
+        doThrow(new IllegalArgumentException("Service not registered")).when(
+                mContext).unbindService(any());
+
+        // Test no exception is thrown
+        transportConnectionMonitor.onBindingDied(mTransportComponent);
+    }
+
     private ServiceConnection verifyBindServiceAsUserAndCaptureServiceConnection(Context context) {
         ArgumentCaptor<ServiceConnection> connectionCaptor =
                 ArgumentCaptor.forClass(ServiceConnection.class);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 1d225ba..221a991 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -36,9 +36,10 @@
 import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import static java.util.Objects.requireNonNull;
+
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -72,7 +73,10 @@
         super.setUp();
         mVisibilityApplier =
                 (DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier();
-        mInputMethodManagerService.setAttachedClientForTesting(mock(ClientState.class));
+        synchronized (ImfLock.class) {
+            mInputMethodManagerService.setAttachedClientForTesting(requireNonNull(
+                    mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient)));
+        }
     }
 
     @Test
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodInfoUtilsTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodInfoUtilsTest.java
new file mode 100644
index 0000000..50804da
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodInfoUtilsTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static com.android.server.inputmethod.TestUtils.TEST_IME_ID1;
+import static com.android.server.inputmethod.TestUtils.TEST_IME_ID2;
+import static com.android.server.inputmethod.TestUtils.createFakeInputMethodInfo;
+import static com.android.server.inputmethod.TestUtils.createFakeSubtypes;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.inputmethod.InputMethodInfo;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+public final class InputMethodInfoUtilsTest {
+
+    @Test
+    public void testMarshalSameObject() {
+        final var imi = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3));
+        final byte[] buf = InputMethodInfoUtils.marshal(imi);
+
+        assertArrayEquals("The same value must be returned when called multiple times",
+                buf, InputMethodInfoUtils.marshal(imi));
+        assertArrayEquals("The same value must be returned when called multiple times",
+                buf, InputMethodInfoUtils.marshal(imi));
+    }
+
+    @Test
+    public void testMarshalDifferentObjects() {
+        final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3));
+        final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(0));
+
+        assertFalse("Different inputs must yield different byte patterns", Arrays.equals(
+                InputMethodInfoUtils.marshal(imi1), InputMethodInfoUtils.marshal(imi2)));
+    }
+
+    @NonNull
+    private static <T> T readTypedObject(byte[] data, @NonNull Parcelable.Creator<T> creator) {
+        Parcel parcel = null;
+        try {
+            parcel = Parcel.obtain();
+            parcel.unmarshall(data, 0, data.length);
+            parcel.setDataPosition(0);
+            return Objects.requireNonNull(parcel.readTypedObject(creator));
+        } finally {
+            if (parcel != null) {
+                parcel.recycle();
+            }
+        }
+    }
+
+    @Test
+    public void testUnmarshalSameObject() {
+        final var imi = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3));
+        final var cloned = readTypedObject(InputMethodInfoUtils.marshal(imi),
+                InputMethodInfo.CREATOR);
+        assertEquals(imi.getPackageName(), cloned.getPackageName());
+        assertEquals(imi.getSubtypeCount(), cloned.getSubtypeCount());
+    }
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index cff2265..3b25cb1 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -29,6 +29,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -45,6 +46,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.view.InputChannel;
 import android.view.inputmethod.EditorInfo;
 import android.window.ImeOnBackInvokedDispatcher;
 
@@ -53,6 +55,7 @@
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.inputmethod.IInputMethod;
 import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IInputMethodSession;
 import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
 import com.android.internal.inputmethod.IRemoteInputConnection;
 import com.android.internal.inputmethod.InputBindResult;
@@ -104,6 +107,7 @@
     @Mock protected UserManagerInternal mMockUserManagerInternal;
     @Mock protected InputMethodBindingController mMockInputMethodBindingController;
     @Mock protected IInputMethodClient mMockInputMethodClient;
+    @Mock protected IInputMethodSession mMockInputMethodSession;
     @Mock protected IBinder mWindowToken;
     @Mock protected IRemoteInputConnection mMockRemoteInputConnection;
     @Mock protected IRemoteAccessibilityInputConnection mMockRemoteAccessibilityInputConnection;
@@ -123,6 +127,7 @@
     protected IInputMethodInvoker mMockInputMethodInvoker;
     protected InputMethodManagerService mInputMethodManagerService;
     protected ServiceThread mServiceThread;
+    protected ServiceThread mPackageMonitorThread;
     protected boolean mIsLargeScreen;
     private InputManagerGlobal.TestSession mInputManagerGlobalSession;
 
@@ -218,10 +223,17 @@
 
         mServiceThread =
                 new ServiceThread(
-                        "TestServiceThread",
-                        Process.THREAD_PRIORITY_FOREGROUND, /* allowIo */
-                        false);
-        mInputMethodManagerService = new InputMethodManagerService(mContext, mServiceThread,
+                        "immstest1",
+                        Process.THREAD_PRIORITY_FOREGROUND,
+                        true /* allowIo */);
+        mPackageMonitorThread =
+                new ServiceThread(
+                        "immstest2",
+                        Process.THREAD_PRIORITY_FOREGROUND,
+                        true /* allowIo */);
+        mInputMethodManagerService = new InputMethodManagerService(mContext,
+                InputMethodManagerService.shouldEnableExperimentalConcurrentMultiUserMode(mContext),
+                mServiceThread, mPackageMonitorThread,
                 unusedUserId -> mMockInputMethodBindingController);
         spyOn(mInputMethodManagerService);
 
@@ -246,6 +258,7 @@
 
         // Call InputMethodManagerService#addClient() as a preparation to start interacting with it.
         mInputMethodManagerService.addClient(mMockInputMethodClient, mMockRemoteInputConnection, 0);
+        createSessionForClient(mMockInputMethodClient);
     }
 
     @After
@@ -254,6 +267,10 @@
             mInputMethodManagerService.mInputMethodDeviceConfigs.destroy();
         }
 
+        if (mPackageMonitorThread != null) {
+            mPackageMonitorThread.quitSafely();
+        }
+
         if (mServiceThread != null) {
             mServiceThread.quitSafely();
         }
@@ -295,4 +312,13 @@
                 .hideSoftInput(any() /* hideInputToken */, notNull() /* statsToken */,
                         anyInt() /* flags */, any() /* resultReceiver */);
     }
+
+    protected void createSessionForClient(IInputMethodClient client) {
+        synchronized (ImfLock.class) {
+            ClientState cs = mInputMethodManagerService.getClientStateLocked(client);
+            cs.mCurSession = new InputMethodManagerService.SessionState(cs,
+                    mMockInputMethodInvoker, mMockInputMethodSession, mock(
+                    InputChannel.class));
+        }
+    }
 }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMapTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMapTest.java
new file mode 100644
index 0000000..be70421
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMapTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static com.android.server.inputmethod.TestUtils.TEST_IME_ID1;
+import static com.android.server.inputmethod.TestUtils.TEST_IME_ID2;
+import static com.android.server.inputmethod.TestUtils.TEST_IME_ID3;
+import static com.android.server.inputmethod.TestUtils.createFakeInputMethodInfo;
+import static com.android.server.inputmethod.TestUtils.createFakeSubtypes;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.util.ArrayMap;
+import android.view.inputmethod.InputMethodInfo;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Test;
+
+public final class InputMethodMapTest {
+
+    @NonNull
+    private static InputMethodMap toMap(InputMethodInfo... list) {
+        final ArrayMap<String, InputMethodInfo> map = new ArrayMap<>();
+        for (var imi : list) {
+            map.put(imi.getId(), imi);
+        }
+        return InputMethodMap.of(map);
+    }
+
+    @Test
+    public void testAreSameSameObject() {
+        final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0));
+        final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3));
+        final var map = toMap(imi1, imi2);
+        assertTrue("Must return true for the same instance",
+                InputMethodMap.areSame(map, map));
+    }
+
+    @Test
+    public void testAreSameEquivalentObject() {
+        final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0));
+        final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3));
+        assertTrue("Must return true for the equivalent instances",
+                InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi1, imi2)));
+
+        assertTrue("Must return true for the equivalent instances",
+                InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi2, imi1)));
+    }
+
+    @Test
+    public void testAreSameDifferentKeys() {
+        final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0));
+        final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3));
+        final var imi3 = createFakeInputMethodInfo(TEST_IME_ID3, createFakeSubtypes(3));
+        assertFalse("Must return false if keys are different",
+                InputMethodMap.areSame(toMap(imi1), toMap(imi1, imi2)));
+        assertFalse("Must return false if keys are different",
+                InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi1)));
+        assertFalse("Must return false if keys are different",
+                InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi1, imi3)));
+    }
+
+    @Test
+    public void testAreSameDifferentValues() {
+        final var imi1_without_subtypes =
+                createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0));
+        final var imi1_with_subtypes =
+                createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3));
+        final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3));
+        assertFalse("Must return false if values are different",
+                InputMethodMap.areSame(toMap(imi1_without_subtypes), toMap(imi1_with_subtypes)));
+        assertFalse("Must return false if values are different",
+                InputMethodMap.areSame(
+                        toMap(imi1_without_subtypes, imi2),
+                        toMap(imi1_with_subtypes, imi2)));
+    }
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/TestUtils.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/TestUtils.java
new file mode 100644
index 0000000..c51ff87f
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/TestUtils.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+public final class TestUtils {
+    /**
+     * {@link ComponentName} for fake {@link InputMethodInfo}.
+     */
+    @NonNull
+    public static final ComponentName TEST_IME_ID1 = Objects.requireNonNull(
+            ComponentName.unflattenFromString("com.android.test.testime1/.InputMethod"));
+
+    /**
+     * {@link ComponentName} for fake {@link InputMethodInfo}.
+     */
+    @NonNull
+    public static final ComponentName TEST_IME_ID2 = Objects.requireNonNull(
+            ComponentName.unflattenFromString("com.android.test.testime2/.InputMethod"));
+
+    /**
+     * {@link ComponentName} for fake {@link InputMethodInfo}.
+     */
+    @NonNull
+    public static final ComponentName TEST_IME_ID3 = Objects.requireNonNull(
+            ComponentName.unflattenFromString("com.android.test.testime3/.InputMethod"));
+
+    /**
+     * Creates a list of fake {@link InputMethodSubtype} for unit testing for the given number.
+     *
+     * @param count The number of fake {@link InputMethodSubtype} objects
+     * @return The list of fake {@link InputMethodSubtype} objects
+     */
+    @NonNull
+    public static ArrayList<InputMethodSubtype> createFakeSubtypes(int count) {
+        final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(count);
+        for (int i = 0; i < count; ++i) {
+            subtypes.add(
+                    new InputMethodSubtype.InputMethodSubtypeBuilder()
+                            .setSubtypeId(i + 0x100)
+                            .setLanguageTag("en-US")
+                            .setSubtypeNameOverride("TestSubtype" + i)
+                            .build());
+        }
+        return subtypes;
+    }
+
+    /**
+     * Creates a fake {@link InputMethodInfo} for unit testing.
+     *
+     * @param componentName {@link ComponentName} of the fake {@link InputMethodInfo}
+     * @param subtypes A list of (fake) {@link InputMethodSubtype}
+     * @return a fake {@link InputMethodInfo} object
+     */
+    @NonNull
+    public static InputMethodInfo createFakeInputMethodInfo(
+            @NonNull ComponentName componentName, @NonNull ArrayList<InputMethodSubtype> subtypes) {
+        final ApplicationInfo ai = new ApplicationInfo();
+        ai.packageName = componentName.getPackageName();
+        ai.enabled = true;
+
+        final ServiceInfo si = new ServiceInfo();
+        si.applicationInfo = ai;
+        si.enabled = true;
+        si.packageName = componentName.getPackageName();
+        si.name = componentName.getClassName();
+        si.exported = true;
+        si.nonLocalizedLabel = "Fake Label";
+
+        final ResolveInfo ri = new ResolveInfo();
+        ri.serviceInfo = si;
+
+        return new InputMethodInfo(ri, false /* isAuxIme */, null /* settingsActivity */,
+                subtypes, 0 /* isDefaultResId */, false /* forceDefault */);
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
index 9e11fa2..e545a49 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
@@ -71,7 +71,7 @@
     @Mock
     private Installer mInstaller;
 
-    private Object mInstallLock;
+    private PackageManagerTracedLock mInstallLock;
 
     @Before
     public void setup() {
@@ -79,7 +79,7 @@
         TEST_USER.serialNumber = TEST_USER_SERIAL;
         Context ctx = InstrumentationRegistry.getContext();
         FileUtils.deleteContents(ctx.getCacheDir());
-        mInstallLock = new Object();
+        mInstallLock = new PackageManagerTracedLock();
         MockitoAnnotations.initMocks(this);
         mUserDataPreparer = new TestUserDataPreparer(mInstaller, mInstallLock, mContextMock,
                 ctx.getCacheDir());
@@ -238,8 +238,8 @@
     private static class TestUserDataPreparer extends UserDataPreparer {
         File testDir;
 
-        TestUserDataPreparer(Installer installer, Object installLock, Context context,
-                File testDir) {
+        TestUserDataPreparer(Installer installer, PackageManagerTracedLock installLock,
+                Context context, File testDir) {
             super(installer, installLock, context);
             this.testDir = testDir;
         }
diff --git a/services/tests/apexsystemservices/OWNERS b/services/tests/apexsystemservices/OWNERS
index 0295b9e..8b6675a 100644
--- a/services/tests/apexsystemservices/OWNERS
+++ b/services/tests/apexsystemservices/OWNERS
@@ -1,4 +1 @@
-omakoto@google.com
-satayev@google.com
-
 include platform/packages/modules/common:/OWNERS
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index a0a611f..46d08b0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -21,7 +21,6 @@
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
 import static com.android.server.display.config.SensorData.TEMPERATURE_TYPE_SKIN;
-import static com.android.server.display.config.SensorData.SupportedMode;
 import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
 import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
 
@@ -58,6 +57,7 @@
 import com.android.server.display.config.HysteresisLevels;
 import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
 import com.android.server.display.config.RefreshRateData;
+import com.android.server.display.config.SupportedModeData;
 import com.android.server.display.config.ThermalStatus;
 import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.feature.flags.Flags;
@@ -613,7 +613,7 @@
         assertEquals(mDisplayDeviceConfig.getProximitySensor().minRefreshRate, 60, SMALL_DELTA);
         assertEquals(mDisplayDeviceConfig.getProximitySensor().maxRefreshRate, 90, SMALL_DELTA);
         assertThat(mDisplayDeviceConfig.getProximitySensor().supportedModes).hasSize(2);
-        SupportedMode mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(0);
+        SupportedModeData mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(0);
         assertEquals(mode.refreshRate, 60, SMALL_DELTA);
         assertEquals(mode.vsyncRate, 65, SMALL_DELTA);
         mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(1);
@@ -933,6 +933,21 @@
         assertEquals(0.2f, mDisplayDeviceConfig.getNitsFromBacklight(0.0f), ZERO_DELTA);
     }
 
+    @Test
+    public void testLowPowerSupportedModesFromConfigFile() throws IOException {
+        setupDisplayDeviceConfigFromDisplayConfigFile();
+
+        RefreshRateData refreshRateData = mDisplayDeviceConfig.getRefreshRateData();
+        assertNotNull(refreshRateData);
+        assertThat(refreshRateData.lowPowerSupportedModes).hasSize(2);
+        SupportedModeData supportedModeData = refreshRateData.lowPowerSupportedModes.get(0);
+        assertThat(supportedModeData.refreshRate).isEqualTo(60);
+        assertThat(supportedModeData.vsyncRate).isEqualTo(60);
+        supportedModeData = refreshRateData.lowPowerSupportedModes.get(1);
+        assertThat(supportedModeData.refreshRate).isEqualTo(60);
+        assertThat(supportedModeData.vsyncRate).isEqualTo(120);
+    }
+
     private String getValidLuxThrottling() {
         return "<luxThrottling>\n"
                 + "    <brightnessLimitMap>\n"
@@ -1089,6 +1104,19 @@
                 + "</proxSensor>\n";
     }
 
+    private String getLowPowerConfig() {
+        return "<lowPowerSupportedModes>\n"
+                + "    <point>\n"
+                + "        <first>60</first>\n"
+                + "        <second>60</second>\n"
+                + "    </point>\n"
+                + "    <point>\n"
+                + "        <first>60</first>\n"
+                + "        <second>120</second>\n"
+                + "    </point>\n"
+                + "</lowPowerSupportedModes>\n";
+    }
+
     private String getHdrBrightnessConfig() {
         return "<hdrBrightnessConfig>\n"
               + "    <brightnessMap>\n"
@@ -1620,6 +1648,7 @@
                 +               "</displayBrightnessPoint>\n"
                 +           "</blockingZoneThreshold>\n"
                 +       "</higherBlockingZoneConfigs>\n"
+                + getLowPowerConfig()
                 +   "</refreshRate>\n"
                 +   "<screenOffBrightnessSensorValueToLux>\n"
                 +       "<item>-1</item>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index ae6361b..df96712 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -46,6 +46,7 @@
 import com.android.server.display.brightness.strategy.BoostBrightnessStrategy;
 import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
 import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
+import com.android.server.display.brightness.strategy.FallbackBrightnessStrategy;
 import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
 import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
 import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
@@ -90,6 +91,8 @@
     @Mock
     private AutoBrightnessFallbackStrategy mAutoBrightnessFallbackStrategy;
     @Mock
+    private FallbackBrightnessStrategy mFallbackBrightnessStrategy;
+    @Mock
     private Resources mResources;
     @Mock
     private DisplayManagerFlags mDisplayManagerFlags;
@@ -135,7 +138,7 @@
 
                 @Override
                 AutomaticBrightnessStrategy getAutomaticBrightnessStrategy1(Context context,
-                        int displayId) {
+                        int displayId, DisplayManagerFlags displayManagerFlags) {
                     return mAutomaticBrightnessStrategy;
                 }
 
@@ -155,6 +158,11 @@
                 AutoBrightnessFallbackStrategy getAutoBrightnessFallbackStrategy() {
                     return mAutoBrightnessFallbackStrategy;
                 }
+
+                @Override
+                FallbackBrightnessStrategy getFallbackBrightnessStrategy() {
+                    return mFallbackBrightnessStrategy;
+                }
             };
 
     @Rule
@@ -355,6 +363,25 @@
     }
 
     @Test
+    public void selectStrategy_selectsFallbackStrategyAsAnUltimateFallback() {
+        when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
+        mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+                mInjector, DISPLAY_ID, mDisplayManagerFlags);
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+                DisplayManagerInternal.DisplayPowerRequest.class);
+        displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+        displayPowerRequest.screenBrightnessOverride = Float.NaN;
+        when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
+        when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+        when(mAutomaticBrightnessStrategy.shouldUseAutoBrightness()).thenReturn(false);
+        when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(false);
+        assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
+                        new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
+                                0.1f, false)),
+                mFallbackBrightnessStrategy);
+    }
+
+    @Test
     public void selectStrategyCallsPostProcessorForAllStrategies() {
         when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
         mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index 3e78118..19bff56 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -18,8 +18,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -45,6 +47,7 @@
 import com.android.server.display.brightness.BrightnessEvent;
 import com.android.server.display.brightness.BrightnessReason;
 import com.android.server.display.brightness.StrategyExecutionRequest;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -64,6 +67,9 @@
     @Mock
     private AutomaticBrightnessController mAutomaticBrightnessController;
 
+    @Mock
+    private DisplayManagerFlags mDisplayManagerFlags;
+
     private BrightnessConfiguration mBrightnessConfiguration;
     private float mDefaultScreenAutoBrightnessAdjustment;
     private Context mContext;
@@ -80,7 +86,8 @@
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, Float.NaN);
         Settings.System.putFloat(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.5f);
-        mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, DISPLAY_ID);
+        mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, DISPLAY_ID,
+                mDisplayManagerFlags);
 
         mBrightnessConfiguration = new BrightnessConfiguration.Builder(
                 new float[]{0f, 1f}, new float[]{0, PowerManager.BRIGHTNESS_ON}).build();
@@ -247,6 +254,46 @@
     }
 
     @Test
+    public void testAutoBrightnessState_modeSwitch() {
+        // Setup the test
+        when(mDisplayManagerFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
+        mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
+        boolean allowAutoBrightnessWhileDozing = false;
+        int brightnessReason = BrightnessReason.REASON_UNKNOWN;
+        float lastUserSetBrightness = 0.2f;
+        boolean userSetBrightnessChanged = true;
+        int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+        float pendingBrightnessAdjustment = 0.1f;
+        Settings.System.putFloat(mContext.getContentResolver(),
+                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
+        mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
+
+        // Validate no interaction when automaticBrightnessController is in idle mode
+        when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(true);
+        mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+                userSetBrightnessChanged);
+        verify(mAutomaticBrightnessController, never()).switchMode(anyInt());
+
+        // Validate interaction when automaticBrightnessController is in non-idle mode, and display
+        // state is ON
+        when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(false);
+        mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+                userSetBrightnessChanged);
+        verify(mAutomaticBrightnessController).switchMode(
+                AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT);
+
+        // Validate interaction when automaticBrightnessController is in non-idle mode, and display
+        // state is DOZE
+        mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+                userSetBrightnessChanged);
+        verify(mAutomaticBrightnessController).switchMode(
+                AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE);
+    }
+
+    @Test
     public void accommodateUserBrightnessChangesWorksAsExpected() {
         // Verify the state if automaticBrightnessController is configured.
         assertFalse(mAutomaticBrightnessStrategy.isShortTermModelActive());
@@ -390,7 +437,8 @@
     @Test
     public void testVerifyNoAutoBrightnessAdjustmentsArePopulatedForNonDefaultDisplay() {
         int newDisplayId = 1;
-        mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, newDisplayId);
+        mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, newDisplayId,
+                mDisplayManagerFlags);
         mAutomaticBrightnessStrategy.putAutoBrightnessAdjustmentSetting(0.3f);
         assertEquals(0.5f, mAutomaticBrightnessStrategy.getAutoBrightnessAdjustment(),
                 0.0f);
@@ -429,8 +477,7 @@
             updateBrightness_constructsDisplayBrightnessState_withAdjustmentAutoAdjustmentFlag() {
         BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
         mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
-                mContext, DISPLAY_ID, displayId -> brightnessEvent);
-        new AutomaticBrightnessStrategy(mContext, DISPLAY_ID);
+                mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
         mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
                 mAutomaticBrightnessController);
         float brightness = 0.4f;
@@ -461,8 +508,7 @@
             updateBrightness_constructsDisplayBrightnessState_withAdjustmentTempAdjustmentFlag() {
         BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
         mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
-                mContext, DISPLAY_ID, displayId -> brightnessEvent);
-        new AutomaticBrightnessStrategy(mContext, DISPLAY_ID);
+                mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
         mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
                 mAutomaticBrightnessController);
         float brightness = 0.4f;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
new file mode 100644
index 0000000..c4767ae
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+
+import static org.junit.Assert.assertEquals;
+
+import android.hardware.display.DisplayManagerInternal;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.StrategyExecutionRequest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+
+public class FallbackBrightnessStrategyTest {
+    private FallbackBrightnessStrategy mFallbackBrightnessStrategy;
+
+    @Before
+    public void before() {
+        mFallbackBrightnessStrategy = new FallbackBrightnessStrategy();
+    }
+
+    @Test
+    public void updateBrightness_currentBrightnessIsSet() {
+        DisplayManagerInternal.DisplayPowerRequest
+                displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
+        float currentBrightness = 0.2f;
+        BrightnessReason brightnessReason = new BrightnessReason();
+        brightnessReason.setReason(BrightnessReason.REASON_MANUAL);
+        DisplayBrightnessState expectedDisplayBrightnessState =
+                new DisplayBrightnessState.Builder()
+                        .setBrightness(currentBrightness)
+                        .setBrightnessReason(brightnessReason)
+                        .setSdrBrightness(currentBrightness)
+                        .setDisplayBrightnessStrategyName(mFallbackBrightnessStrategy.getName())
+                        .setShouldUpdateScreenBrightnessSetting(true)
+                        .build();
+        DisplayBrightnessState updatedDisplayBrightnessState =
+                mFallbackBrightnessStrategy.updateBrightness(
+                        new StrategyExecutionRequest(displayPowerRequest, currentBrightness));
+        assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
+    }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
index a785300..27f87aa 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
@@ -161,4 +161,20 @@
                 .isEqualTo(Integer.toString(testPropertyValue));
     }
 
+    @Test
+    public void daltonizer_defaultValues() {
+        synchronized (mDtm.mDaltonizerModeLock) {
+            assertThat(mDtm.mDaltonizerMode).isEqualTo(-1);
+            assertThat(mDtm.mDaltonizerLevel).isEqualTo(-1);
+        }
+    }
+
+    @Test
+    public void setDaltonizerMode_newValues_valuesUpdated() {
+        mDtm.setDaltonizerMode(0, 0);
+        synchronized (mDtm.mDaltonizerModeLock) {
+            assertThat(mDtm.mDaltonizerMode).isEqualTo(0);
+            assertThat(mDtm.mDaltonizerLevel).isEqualTo(0);
+        }
+    }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index cd1e9e8..714b423 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -131,7 +131,8 @@
             /* defaultRefreshRate= */ 0,
             /* defaultPeakRefreshRate= */ 0,
             /* defaultRefreshRateInHbmHdr= */ 0,
-            /* defaultRefreshRateInHbmSunlight= */ 0);
+            /* defaultRefreshRateInHbmSunlight= */ 0,
+            /* lowPowerSupportedModes =*/ List.of());
 
     public static Collection<Object[]> getAppRequestedSizeTestCases() {
         var appRequestedSizeTestCases = Arrays.asList(new Object[][] {
@@ -157,7 +158,7 @@
                                         APP_MODE_HIGH_90.getPhysicalHeight()),
                                 Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
                                 Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
-                                Vote.PRIORITY_LOW_POWER_MODE,
+                                Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
                                 Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
                                         LIMIT_MODE_70.getPhysicalHeight()))},
                 {/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
@@ -169,7 +170,7 @@
                                         APP_MODE_65.getPhysicalHeight()),
                                 Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
                                 Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
-                                Vote.PRIORITY_LOW_POWER_MODE,
+                                Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
                                 Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
                                         LIMIT_MODE_70.getPhysicalHeight()))},
                 {/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
@@ -181,7 +182,7 @@
                                         APP_MODE_65.getPhysicalHeight()),
                                 Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
                                 Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
-                                Vote.PRIORITY_LOW_POWER_MODE,
+                                Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
                                 Vote.forSizeAndPhysicalRefreshRatesRange(
                                     0, 0,
                                     LIMIT_MODE_70.getPhysicalWidth(),
@@ -197,7 +198,7 @@
                                         APP_MODE_65.getPhysicalHeight()),
                                 Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
                                 Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
-                                Vote.PRIORITY_LOW_POWER_MODE,
+                                Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
                                 Vote.forSizeAndPhysicalRefreshRatesRange(
                                     0, 0,
                                     LIMIT_MODE_70.getPhysicalWidth(),
@@ -213,7 +214,7 @@
                                         APP_MODE_HIGH_90.getPhysicalHeight()),
                                 Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
                                 Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
-                                Vote.PRIORITY_LOW_POWER_MODE,
+                                Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
                                 Vote.forSizeAndPhysicalRefreshRatesRange(
                                     0, 0,
                                     LIMIT_MODE_70.getPhysicalWidth(),
@@ -229,7 +230,7 @@
                                         APP_MODE_HIGH_90.getPhysicalHeight()),
                                 Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
                                 Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
-                                Vote.PRIORITY_LOW_POWER_MODE,
+                                Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
                                 Vote.forSizeAndPhysicalRefreshRatesRange(
                                     0, 0,
                                     LIMIT_MODE_70.getPhysicalWidth(),
@@ -245,7 +246,7 @@
                                 Vote.PRIORITY_APP_REQUEST_SIZE,
                                 Vote.forSize(APP_MODE_65.getPhysicalWidth(),
                                         APP_MODE_65.getPhysicalHeight()),
-                                Vote.PRIORITY_LOW_POWER_MODE,
+                                Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
                                 Vote.forPhysicalRefreshRates(
                                         0, 64.99f))}});
 
@@ -598,7 +599,7 @@
                 < Vote.PRIORITY_APP_REQUEST_SIZE);
 
         assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH
-                > Vote.PRIORITY_LOW_POWER_MODE);
+                > Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE);
 
         Display.Mode[] modes = new Display.Mode[4];
         modes[0] = new Display.Mode(
@@ -676,9 +677,9 @@
 
     @Test
     public void testLPMHasHigherPriorityThanUser() {
-        assertTrue(Vote.PRIORITY_LOW_POWER_MODE
+        assertTrue(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE
                 > Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertTrue(Vote.PRIORITY_LOW_POWER_MODE
+        assertTrue(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE
                 > Vote.PRIORITY_APP_REQUEST_SIZE);
 
         Display.Mode[] modes = new Display.Mode[4];
@@ -700,7 +701,7 @@
                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
                 appRequestedMode.getPhysicalHeight()));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.baseModeId).isEqualTo(2);
@@ -715,7 +716,7 @@
                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
                 appRequestedMode.getPhysicalHeight()));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.baseModeId).isEqualTo(4);
@@ -730,7 +731,7 @@
                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
                 appRequestedMode.getPhysicalHeight()));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.baseModeId).isEqualTo(2);
@@ -745,7 +746,7 @@
                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
                 appRequestedMode.getPhysicalHeight()));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.baseModeId).isEqualTo(4);
@@ -906,7 +907,7 @@
                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
         votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
                 Vote.forRenderFrameRates(60, 60));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
         director.injectVotesByDisplay(votesByDisplay);
 
         assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
@@ -946,7 +947,7 @@
         votesByDisplay.put(DISPLAY_ID, votes);
         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
                 Vote.forRenderFrameRates(30, 90));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
 
         director.injectVotesByDisplay(votesByDisplay);
         assertThat(director.getModeSwitchingType())
@@ -987,7 +988,7 @@
         votesByDisplay.put(DISPLAY_ID, votes);
         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
                 Vote.forRenderFrameRates(30, 90));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
 
         director.injectVotesByDisplay(votesByDisplay);
         assertThat(director.getModeSwitchingType())
@@ -1029,7 +1030,7 @@
         votesByDisplay.put(DISPLAY_ID, votes);
         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
                 Vote.forRenderFrameRates(30, 90));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
 
         director.injectVotesByDisplay(votesByDisplay);
         assertThat(director.getModeSwitchingType())
@@ -1900,7 +1901,7 @@
         director.start(createMockSensorManager());
 
         SparseArray<Vote> votes = new SparseArray<>();
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 50f));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 50f));
 
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
         votesByDisplay.put(DISPLAY_ID_2, votes);
@@ -2298,7 +2299,7 @@
         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
                 Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY));
         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(50);
@@ -2311,7 +2312,7 @@
         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
                 Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY));
         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 90));
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(80);
         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(80);
@@ -2323,7 +2324,7 @@
         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
                 Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY));
         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 90));
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
@@ -2343,7 +2344,7 @@
         votesByDisplay.put(DISPLAY_ID, votes);
         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
                 Vote.forBaseModeRefreshRate(70));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2360,7 +2361,7 @@
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
                 Vote.forBaseModeRefreshRate(55));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2374,7 +2375,7 @@
                 Vote.forRenderFrameRates(0, 52));
         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
                 Vote.forBaseModeRefreshRate(55));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2392,7 +2393,7 @@
                 Vote.forRenderFrameRates(0, 58));
         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
                 Vote.forBaseModeRefreshRate(55));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2521,7 +2522,7 @@
 
 
         votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(120, 120));
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90));
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(120);
@@ -2542,7 +2543,7 @@
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
         votesByDisplay.put(DISPLAY_ID, votes);
 
-        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
         votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
                 Vote.forRenderFrameRates(0, 30));
         director.injectVotesByDisplay(votesByDisplay);
@@ -3168,7 +3169,8 @@
                 /* defaultRefreshRate= */ 60,
                 /* defaultPeakRefreshRate= */ 65,
                 /* defaultRefreshRateInHbmHdr= */ 65,
-                /* defaultRefreshRateInHbmSunlight= */ 75);
+                /* defaultRefreshRateInHbmSunlight= */ 75,
+                /* lowPowerSupportedModes= */ List.of());
         when(displayDeviceConfig.getRefreshRateData()).thenReturn(refreshRateData);
         when(displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
         when(displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55);
@@ -3390,9 +3392,10 @@
 
         ArgumentCaptor<DisplayListener> displayListenerCaptor =
                 ArgumentCaptor.forClass(DisplayListener.class);
-        verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+        verify(mInjector, atLeastOnce()).registerDisplayListener(displayListenerCaptor.capture(),
                 any(Handler.class));
-        DisplayListener displayListener = displayListenerCaptor.getValue();
+        // DisplayObserver should register first
+        DisplayListener displayListener = displayListenerCaptor.getAllValues().get(0);
 
         float refreshRate = 60;
         mInjector.mDisplayInfo.layoutLimitedRefreshRate =
@@ -3417,9 +3420,10 @@
 
         ArgumentCaptor<DisplayListener> displayListenerCaptor =
                 ArgumentCaptor.forClass(DisplayListener.class);
-        verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+        verify(mInjector, atLeastOnce()).registerDisplayListener(displayListenerCaptor.capture(),
                 any(Handler.class));
-        DisplayListener displayListener = displayListenerCaptor.getValue();
+        // DisplayObserver should register first
+        DisplayListener displayListener = displayListenerCaptor.getAllValues().get(0);
 
         mInjector.mDisplayInfo.layoutLimitedRefreshRate = new RefreshRateRange(10, 10);
         mInjector.mDisplayInfoValid = false;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
index 2d317af..ee79d19 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -407,7 +407,8 @@
             assertThat(mObserver).isNull();
             mObserver = invocation.getArgument(0);
             return null;
-        }).when(mInjector).registerDisplayListener(any(), any());
+        }).when(mInjector).registerDisplayListener(
+                any(DisplayModeDirector.DisplayObserver.class), any());
 
         doAnswer(c -> {
             DisplayInfo info = c.getArgument(1);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
index 4d910ce..e431c8c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
@@ -27,8 +27,11 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.util.test.FakeSettingsProvider
 import com.android.server.display.DisplayDeviceConfig
+import com.android.server.display.config.RefreshRateData
+import com.android.server.display.config.SupportedModeData
 import com.android.server.display.feature.DisplayManagerFlags
 import com.android.server.display.mode.DisplayModeDirector.DisplayDeviceConfigProvider
+import com.android.server.display.mode.SupportedRefreshRatesVote.RefreshRates
 import com.android.server.testutils.TestHandler
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -69,6 +72,13 @@
 private val RANGES_MIN60_60TO90 = RefreshRateRanges(RANGE_60_INF, RANGE_60_90)
 private val RANGES_MIN90_90TO90 = RefreshRateRanges(RANGE_90_INF, RANGE_90_90)
 
+private val LOW_POWER_GLOBAL_VOTE = Vote.forRenderFrameRates(0f, 60f)
+private val LOW_POWER_REFRESH_RATE_DATA = createRefreshRateData(
+    lowPowerSupportedModes = listOf(SupportedModeData(60f, 60f), SupportedModeData(60f, 240f)))
+private val LOW_POWER_EMPTY_REFRESH_RATE_DATA = createRefreshRateData()
+private val EXPECTED_SUPPORTED_MODES_VOTE = SupportedRefreshRatesVote(
+    listOf(RefreshRates(60f, 60f), RefreshRates(60f, 240f)))
+
 @SmallTest
 @RunWith(TestParameterInjector::class)
 class SettingsObserverTest {
@@ -103,7 +113,7 @@
         val displayModeDirector = DisplayModeDirector(
                 spyContext, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider)
         val ddcByDisplay = SparseArray<DisplayDeviceConfig>()
-        whenever(mockDeviceConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported)
+        whenever(mockDeviceConfig.refreshRateData).thenReturn(testCase.refreshRateData)
         ddcByDisplay.put(Display.DEFAULT_DISPLAY, mockDeviceConfig)
         displayModeDirector.injectDisplayDeviceConfigByDisplay(ddcByDisplay)
         val settingsObserver = displayModeDirector.SettingsObserver(
@@ -113,27 +123,30 @@
                 false, Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), 1)
 
         assertThat(displayModeDirector.getVote(VotesStorage.GLOBAL_ID,
-                Vote.PRIORITY_LOW_POWER_MODE)).isEqualTo(testCase.expectedVote)
+                Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE)).isEqualTo(testCase.globalVote)
+        assertThat(displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
+            Vote.PRIORITY_LOW_POWER_MODE_MODES)).isEqualTo(testCase.displayVote)
     }
 
     enum class LowPowerTestCase(
-        val vrrSupported: Boolean,
+        val refreshRateData: RefreshRateData,
         val vsyncLowPowerVoteEnabled: Boolean,
         val lowPowerModeEnabled: Boolean,
-        internal val expectedVote: Vote?
+        internal val globalVote: Vote?,
+        internal val displayVote: Vote?
     ) {
-        ALL_ENABLED(true, true, true,
-            SupportedRefreshRatesVote(listOf(
-                SupportedRefreshRatesVote.RefreshRates(60f, 240f),
-                SupportedRefreshRatesVote.RefreshRates(60f, 60f)
-            ))),
-        LOW_POWER_OFF(true, true, false, null),
-        DVRR_NOT_SUPPORTED_LOW_POWER_ON(false, true, true,
-            RefreshRateVote.RenderVote(0f, 60f)),
-        DVRR_NOT_SUPPORTED_LOW_POWER_OFF(false, true, false, null),
-        VSYNC_VOTE_DISABLED_SUPPORTED_LOW_POWER_ON(true, false, true,
-            RefreshRateVote.RenderVote(0f, 60f)),
-        VSYNC_VOTE_DISABLED_LOW_POWER_OFF(true, false, false, null),
+        ALL_ENABLED(LOW_POWER_REFRESH_RATE_DATA, true, true,
+            LOW_POWER_GLOBAL_VOTE, EXPECTED_SUPPORTED_MODES_VOTE),
+        LOW_POWER_OFF(LOW_POWER_REFRESH_RATE_DATA, true, false,
+            null, null),
+        EMPTY_REFRESH_LOW_POWER_ON(LOW_POWER_EMPTY_REFRESH_RATE_DATA, true, true,
+            LOW_POWER_GLOBAL_VOTE, null),
+        EMPTY_REFRESH__LOW_POWER_OFF(LOW_POWER_EMPTY_REFRESH_RATE_DATA, true, false,
+            null, null),
+        VSYNC_VOTE_DISABLED_SUPPORTED_LOW_POWER_ON(LOW_POWER_REFRESH_RATE_DATA, false, true,
+            LOW_POWER_GLOBAL_VOTE, null),
+        VSYNC_VOTE_DISABLED_LOW_POWER_OFF(LOW_POWER_REFRESH_RATE_DATA, false, false,
+            null, null),
     }
 
     @Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
index 6b90bde..1206e30 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
@@ -16,6 +16,9 @@
 
 package com.android.server.display.mode
 
+import com.android.server.display.config.RefreshRateData
+import com.android.server.display.config.SupportedModeData
+
 internal fun createVotesSummary(
         isDisplayResolutionRangeVotingEnabled: Boolean = true,
         supportedModesVoteEnabled: Boolean = true,
@@ -24,4 +27,16 @@
 ): VoteSummary {
     return VoteSummary(isDisplayResolutionRangeVotingEnabled, supportedModesVoteEnabled,
             loggingEnabled, supportsFrameRateOverride)
-}
\ No newline at end of file
+}
+
+fun createRefreshRateData(
+        defaultRefreshRate: Int = 60,
+        defaultPeakRefreshRate: Int = 60,
+        defaultRefreshRateInHbmHdr: Int = 60,
+        defaultRefreshRateInHbmSunlight: Int = 60,
+        lowPowerSupportedModes: List<SupportedModeData> = emptyList()
+): RefreshRateData {
+        return RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate,
+                defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight,
+                lowPowerSupportedModes)
+}
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
index 88ab871..874e991 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
@@ -273,28 +273,36 @@
     }
 
     @Test
-    public void setDreamHasFocus_true_dreamHasFocus() {
+    public void setDreamIsObscured_true_dreamIsNotFrontmost() {
         mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
 
-        mDreamController.setDreamHasFocus(true);
-        assertTrue(mDreamController.dreamHasFocus());
+        mDreamController.setDreamIsObscured(true);
+        assertFalse(mDreamController.dreamIsFrontmost());
     }
 
     @Test
-    public void setDreamHasFocus_false_dreamDoesNotHaveFocus() {
+    public void setDreamIsObscured_false_dreamIsFrontmost() {
         mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
 
-        mDreamController.setDreamHasFocus(false);
-        assertFalse(mDreamController.dreamHasFocus());
+        mDreamController.setDreamIsObscured(false);
+        assertTrue(mDreamController.dreamIsFrontmost());
     }
 
     @Test
-    public void setDreamHasFocus_notDreaming_dreamDoesNotHaveFocus() {
-        mDreamController.setDreamHasFocus(true);
-        // Dream still doesn't have focus because it was never started.
-        assertFalse(mDreamController.dreamHasFocus());
+    public void setDreamIsObscured_notDreaming_dreamIsNotFrontmost() {
+        mDreamController.setDreamIsObscured(true);
+        // Dream still isn't frontmost because it was never started.
+        assertFalse(mDreamController.dreamIsFrontmost());
+    }
+
+    @Test
+    public void startDream_dreamIsFrontmost() {
+        mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
+                0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+
+        assertTrue(mDreamController.dreamIsFrontmost());
     }
 
     private ServiceConnection captureServiceConnection() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index cc69c1d..28c7fb2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -956,6 +956,7 @@
         ConnectionRecord cr = s.getConnections().get(binder).get(0);
         setFieldValue(ConnectionRecord.class, cr, "activity",
                 mock(ActivityServiceConnectionsHolder.class));
+        doReturn(client).when(sService).getTopApp();
         doReturn(true).when(cr.activity).isActivityVisible();
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index c9aab53..396edae 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -186,7 +186,7 @@
 
     class Mocks {
         val lock = PackageManagerTracedLock()
-        val installLock = Any()
+        val installLock = PackageManagerTracedLock()
         val injector: PackageManagerServiceInjector = mock()
         val systemWrapper: PackageManagerServiceInjector.SystemWrapper = mock()
         val context: Context = mock()
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 7d58a2e..79f1574 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -778,49 +778,6 @@
     }
 
     @Test
-    public void testGetAliveUsers_shouldExcludeInitialisedEphemeralNonCurrentUsers() {
-        assertWithMessage("Ephemeral user should not exist at all initially")
-                .that(mUmi.getUsers(false).stream().anyMatch(u -> u.id == USER_ID))
-                .isFalse();
-
-        // add an ephemeral full user
-        TestUserData userData = new TestUserData(USER_ID);
-        userData.info.flags = UserInfo.FLAG_FULL | UserInfo.FLAG_EPHEMERAL;
-        addUserData(userData);
-
-        assertWithMessage("Ephemeral user should exist as alive after being created")
-                .that(mUmi.getUsers(true).stream().anyMatch(u -> u.id == USER_ID))
-                .isTrue();
-
-        // mock switch to the user (mark it as initialized & make it the current user)
-        userData.info.flags |= UserInfo.FLAG_INITIALIZED;
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("Ephemeral user should still exist as alive after being switched to")
-                .that(mUmi.getUsers(true).stream().anyMatch(u -> u.id == USER_ID))
-                .isTrue();
-
-        // switch away from the user
-        mockCurrentUser(OTHER_USER_ID);
-
-        assertWithMessage("Ephemeral user should not exist as alive after getting switched away")
-                .that(mUmi.getUsers(true).stream().anyMatch(u -> u.id == USER_ID))
-                .isFalse();
-
-        assertWithMessage("Ephemeral user should still exist as dying after getting switched away")
-                .that(mUmi.getUsers(false).stream().anyMatch(u -> u.id == USER_ID))
-                .isTrue();
-
-        // finally remove the user
-        mUms.removeUserInfo(USER_ID);
-
-        assertWithMessage("Ephemeral user should not exist at all after cleanup")
-                .that(mUmi.getUsers(false).stream().anyMatch(u -> u.id == USER_ID))
-                .isFalse();
-    }
-
-
-    @Test
     @RequiresFlagsEnabled({android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
             Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION, Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
     public void testCreatePrivateProfileOnHeadlessSystemUser_shouldAllowCreation() {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
index d29bf1a..3635e9a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.os.BatteryManager;
@@ -49,9 +50,9 @@
     private static final int BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL = 100;
 
     private MockClock mMockClock;
+    private BatteryStatsImpl.BatteryStatsConfig mConfig;
     private MockBatteryStatsImpl mBatteryStatsImpl;
 
-
     /**
      * Battery status. Must be one of the following:
      * {@link BatteryManager#BATTERY_STATUS_UNKNOWN}
@@ -91,8 +92,9 @@
 
     @Before
     public void setUp() throws IOException {
+        mConfig = mock(BatteryStatsImpl.BatteryStatsConfig.class);
         mMockClock = new MockClock();
-        mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock,
+        mBatteryStatsImpl = new MockBatteryStatsImpl(mConfig, mMockClock,
                 Files.createTempDirectory("BatteryStatsResetTest").toFile());
         mBatteryStatsImpl.onSystemReady(mock(Context.class));
 
@@ -110,10 +112,7 @@
 
     @Test
     public void testResetOnUnplug_highBatteryLevel() {
-        mBatteryStatsImpl.setBatteryStatsConfig(
-                new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                        .setResetOnUnplugHighBatteryLevel(true)
-                        .build());
+        when(mConfig.shouldResetOnUnplugHighBatteryLevel()).thenReturn(true);
 
         long expectedResetTimeUs = 0;
 
@@ -137,10 +136,7 @@
         assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
 
         // disable high battery level reset on unplug.
-        mBatteryStatsImpl.setBatteryStatsConfig(
-                new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                        .setResetOnUnplugHighBatteryLevel(false)
-                        .build());
+        when(mConfig.shouldResetOnUnplugHighBatteryLevel()).thenReturn(false);
 
         dischargeToLevel(60);
 
@@ -153,10 +149,7 @@
 
     @Test
     public void testResetOnUnplug_significantCharge() {
-        mBatteryStatsImpl.setBatteryStatsConfig(
-                new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                        .setResetOnUnplugAfterSignificantCharge(true)
-                        .build());
+        when(mConfig.shouldResetOnUnplugAfterSignificantCharge()).thenReturn(true);
         long expectedResetTimeUs = 0;
 
         unplugBattery();
@@ -186,10 +179,7 @@
         assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
 
         // disable reset on unplug after significant charge.
-        mBatteryStatsImpl.setBatteryStatsConfig(
-                new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                        .setResetOnUnplugAfterSignificantCharge(false)
-                        .build());
+        when(mConfig.shouldResetOnUnplugAfterSignificantCharge()).thenReturn(false);
 
         // Battery level dropped below 20%.
         dischargeToLevel(15);
@@ -256,11 +246,9 @@
     @Test
     public void testResetWhilePluggedIn_longPlugIn() {
         // disable high battery level reset on unplug.
-        mBatteryStatsImpl.setBatteryStatsConfig(
-                new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                        .setResetOnUnplugHighBatteryLevel(false)
-                        .setResetOnUnplugAfterSignificantCharge(false)
-                        .build());
+        when(mConfig.shouldResetOnUnplugHighBatteryLevel()).thenReturn(false);
+        when(mConfig.shouldResetOnUnplugAfterSignificantCharge()).thenReturn(false);
+
         long expectedResetTimeUs = 0;
 
         plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 2d7cb22..6edfede 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -98,10 +98,12 @@
         mFreqsByPolicy.put(0, new int[]{300000, 1000000, 2000000});
         mFreqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000});
         mBatteryStatsConfigBuilder = new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU,
-                        10000)
-                .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
-                        10000);
+                .setPowerStatsThrottlePeriodMillis(
+                        BatteryConsumer.powerComponentIdToString(
+                                BatteryConsumer.POWER_COMPONENT_CPU), 10000)
+                .setPowerStatsThrottlePeriodMillis(
+                        BatteryConsumer.powerComponentIdToString(
+                                BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO), 10000);
     }
 
     private void initBatteryStats() {
@@ -290,7 +292,8 @@
 
     public BatteryUsageStatsRule setPowerStatsThrottlePeriodMillis(int powerComponent,
             long throttleMs) {
-        mBatteryStatsConfigBuilder.setPowerStatsThrottlePeriodMillis(powerComponent, throttleMs);
+        mBatteryStatsConfigBuilder.setPowerStatsThrottlePeriodMillis(
+                BatteryConsumer.powerComponentIdToString(powerComponent), throttleMs);
         return this;
     }
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
index 4e3e80f..d1105a4 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
@@ -32,6 +32,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.IndentingPrintWriter;
 import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
@@ -51,6 +52,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.io.StringWriter;
 import java.util.function.IntSupplier;
 
 @RunWith(AndroidJUnit4.class)
@@ -127,6 +129,11 @@
         }
 
         @Override
+        public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+            return 0;
+        }
+
+        @Override
         public int getDefaultCpuPowerBrackets() {
             return mDefaultCpuPowerBrackets;
         }
@@ -363,6 +370,36 @@
                 .isEqualTo(528);
     }
 
+    @Test
+    public void dump() {
+        mockCpuScalingPolicies(1);
+        mockPowerProfile();
+        mockEnergyConsumers();
+
+        CpuPowerStatsCollector collector = createCollector(8, 0);
+        collector.collectStats();       // Establish baseline
+
+        mockKernelCpuStats(new long[]{1111, 2222, 3333},
+                new SparseArray<>() {{
+                    put(UID_1, new long[]{100, 200});
+                    put(UID_2, new long[]{100, 150});
+                    put(ISOLATED_UID, new long[]{200, 450});
+                }}, 0, 1234);
+
+        PowerStats powerStats = collector.collectStats();
+
+        StringWriter sw = new StringWriter();
+        IndentingPrintWriter pw = new IndentingPrintWriter(sw);
+        powerStats.dump(pw);
+        pw.flush();
+        String dump = sw.toString();
+
+        assertThat(dump).contains("duration=1234");
+        assertThat(dump).contains("steps: [1111, 2222, 3333]");
+        assertThat(dump).contains("UID 42: time: [100, 200]");
+        assertThat(dump).contains("UID 99: time: [300, 600]");
+    }
+
     private void mockCpuScalingPolicies(int clusterCount) {
         SparseArray<int[]> cpus = new SparseArray<>();
         SparseArray<int[]> freqs = new SparseArray<>();
@@ -386,8 +423,8 @@
     private CpuPowerStatsCollector createCollector(int defaultCpuPowerBrackets,
             int defaultCpuPowerBracketsPerEnergyConsumer) {
         CpuPowerStatsCollector collector = new CpuPowerStatsCollector(
-                new TestInjector(defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer),
-                0);
+                new TestInjector(defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer)
+        );
         collector.addConsumer(stats -> mCollectedStats = stats);
         collector.setEnabled(true);
         return collector;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
index 70c40f5..644ae47 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
@@ -25,6 +25,7 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -46,7 +47,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
@@ -92,9 +95,10 @@
         long duration = 0;
         long[] stats = null;
 
-        String[] cpuStatsDump = dumpCpuStats();
+        List<String> cpuStatsDump = dumpCpuStats();
         Pattern durationPattern = Pattern.compile("duration=([0-9]*)");
-        Pattern uidPattern = Pattern.compile("UID " + mTestPkgUid + ": \\[([0-9,\\s]*)]");
+        Pattern uidPattern = Pattern.compile(
+                "UID " + UserHandle.formatUid(mTestPkgUid) + ": time: [\\[]?([0-9,\\s]*)[]]?");
         for (String line : cpuStatsDump) {
             Matcher durationMatcher = durationPattern.matcher(line);
             if (durationMatcher.find()) {
@@ -119,15 +123,23 @@
         assertThat(total).isAtLeast((long) (WORK_DURATION_MS * 0.8));
     }
 
-    private String[] dumpCpuStats() throws Exception {
+    private List<String> dumpCpuStats() throws Exception {
+        ArrayList<String> cpuStats = new ArrayList<>();
         String dump = executeCmdSilent("dumpsys batterystats --sample");
         String[] lines = dump.split("\n");
+        boolean inCpuSection = false;
         for (int i = 0; i < lines.length; i++) {
-            if (lines[i].startsWith("CpuPowerStatsCollector")) {
-                return Arrays.copyOfRange(lines, i + 1, lines.length);
+            if (!inCpuSection) {
+                if (lines[i].startsWith("CpuPowerStatsCollector")) {
+                    inCpuSection = true;
+                }
+            } else if (lines[i].startsWith(" ")) {
+                cpuStats.add(lines[i]);
+            } else {
+                break;
             }
         }
-        return new String[0];
+        return cpuStats;
     }
 
     private void doSomeWork() throws Exception {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
index f93c4da..0275319 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
@@ -123,6 +123,11 @@
         }
 
         @Override
+        public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+            return 0;
+        }
+
+        @Override
         public PackageManager getPackageManager() {
             return mPackageManager;
         }
@@ -337,16 +342,18 @@
         pw.flush();
         String dump = sw.toString();
         assertThat(dump).contains("duration=100");
+        assertThat(dump).contains("sleep: 200 idle: 300 scan: 60000 call: 40000 energy: "
+                + ((64321 - 10000) * 1000 / 3500));
+        assertThat(dump).contains("(LTE) rx: 7000 tx: [8000, 9000, 1000, 2000, 3000]");
+        assertThat(dump).contains("(NR MMWAVE) rx: 6000 tx: [1000, 2000, 3000, 4000, 5000]");
         assertThat(dump).contains(
-                "stats=[200, 300, 60000, 40000, " + ((64321 - 10000) * 1000 / 3500) + ", 0, 0, 0]");
-        assertThat(dump).contains("state LTE: [7000, 8000, 9000, 1000, 2000, 3000]");
-        assertThat(dump).contains("state NR MMWAVE: [6000, 1000, 2000, 3000, 4000, 5000]");
-        assertThat(dump).contains("UID 24: [6000, 3000, 60, 30, 0]");
-        assertThat(dump).contains("UID 42: [1000, 2000, 100, 200, 0]");
+                "UID 24: rx-pkts: 60 rx-B: 6000 tx-pkts: 30 tx-B: 3000");
+        assertThat(dump).contains(
+                "UID 42: rx-pkts: 100 rx-B: 1000 tx-pkts: 200 tx-B: 2000");
     }
 
     private PowerStats collectPowerStats(boolean perNetworkTypeData) throws Throwable {
-        MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+        MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector);
         collector.setEnabled(true);
 
         when(mConsumedEnergyRetriever.getEnergyConsumerIds(
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
index 4ac7ad8..29ef3b6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
@@ -114,6 +114,11 @@
                 }
 
                 @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
                 public PackageManager getPackageManager() {
                     return mPackageManager;
                 }
@@ -186,7 +191,7 @@
         aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
         aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
 
-        MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+        MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector);
         collector.setEnabled(true);
 
         // Initial empty ModemActivityInfo.
@@ -305,79 +310,9 @@
     }
 
     @Test
-    public void measuredEnergyModel() {
-        // PowerStats hardware is available
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
-                .thenReturn(new int[] {MOBILE_RADIO_ENERGY_CONSUMER_ID});
-
-        mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
-                .initMeasuredEnergyStatsLocked();
-
-        MobileRadioPowerStatsProcessor processor =
-                new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
-
-        AggregatedPowerStatsConfig.PowerComponent config =
-                new AggregatedPowerStatsConfig.PowerComponent(
-                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
-                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                        .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
-                        .setProcessor(processor);
-
+    public void energyConsumerModel() {
         PowerComponentAggregatedPowerStats aggregatedStats =
-                new PowerComponentAggregatedPowerStats(
-                        new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
-
-        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
-        aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
-
-        MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
-        collector.setEnabled(true);
-
-        // Initial empty ModemActivityInfo.
-        mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
-
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(
-                new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID}))
-                .thenReturn(new long[]{0});
-
-        // Establish a baseline
-        aggregatedStats.addPowerStats(collector.collectStats(), 0);
-
-        // Turn the screen off after 2.5 seconds
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
-                5000);
-
-        // Note application network activity
-        NetworkStats networkStats = mockNetworkStats(10000, 1,
-                mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
-                mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
-
-        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
-
-        ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
-                new int[]{100, 200, 300, 400, 500}, 600);
-        mockModemActivityInfo(mai);
-
-        mStatsRule.setTime(10_000, 10_000);
-
-        long energyUws = 10_000_000L * VOLTAGE_MV / 1000L;
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(
-                new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
-
-        when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
-        when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
-
-        PowerStats powerStats = collector.collectStats();
-
-        aggregatedStats.addPowerStats(powerStats, 10_000);
-
-        processor.finish(aggregatedStats);
+                prepareAggregatedStats_energyConsumerModel();
 
         MobileRadioPowerStatsLayout statsLayout =
                 new MobileRadioPowerStatsLayout(
@@ -448,6 +383,102 @@
                 .isWithin(PRECISION).of(0.75);
     }
 
+    @Test
+    public void test_toString() {
+        PowerComponentAggregatedPowerStats stats = prepareAggregatedStats_energyConsumerModel();
+        String string = stats.toString();
+        assertThat(string).contains("(pwr-other scr-on)"
+                + " sleep: 500 idle: 750 scan: 1388 call: 50 energy: 2500000 power: 0.672");
+        assertThat(string).contains("(pwr-other scr-other)"
+                + " sleep: 1500 idle: 2250 scan: 4166 call: 150 energy: 7500000 power: 2.02");
+        assertThat(string).contains("(pwr-other scr-on other)"
+                + " rx: 150 tx: [25, 50, 75, 100, 125]");
+        assertThat(string).contains("(pwr-other scr-other other)"
+                + " rx: 450 tx: [75, 150, 225, 300, 375]");
+        assertThat(string).contains("(pwr-other scr-on fg)"
+                + " rx-pkts: 375 rx-B: 2500 tx-pkts: 75 tx-B: 5000 power: 0.198");
+        assertThat(string).contains("(pwr-other scr-other bg)"
+                + " rx-pkts: 375 rx-B: 2500 tx-pkts: 75 tx-B: 5000 power: 0.198");
+        assertThat(string).contains("(pwr-other scr-other fgs)"
+                + " rx-pkts: 750 rx-B: 5000 tx-pkts: 150 tx-B: 10000 power: 0.396");
+    }
+
+    private PowerComponentAggregatedPowerStats prepareAggregatedStats_energyConsumerModel() {
+        // PowerStats hardware is available
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
+                .thenReturn(new int[] {MOBILE_RADIO_ENERGY_CONSUMER_ID});
+
+        mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
+                .initMeasuredEnergyStatsLocked();
+
+        MobileRadioPowerStatsProcessor processor =
+                new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
+
+        AggregatedPowerStatsConfig.PowerComponent config =
+                new AggregatedPowerStatsConfig.PowerComponent(
+                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                        .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+                        .setProcessor(processor);
+
+        PowerComponentAggregatedPowerStats aggregatedStats =
+                new PowerComponentAggregatedPowerStats(
+                        new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+
+        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+        aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector);
+        collector.setEnabled(true);
+
+        // Initial empty ModemActivityInfo.
+        mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
+
+        when(mConsumedEnergyRetriever.getConsumedEnergyUws(
+                new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID}))
+                .thenReturn(new long[]{0});
+
+        // Establish a baseline
+        aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+        // Turn the screen off after 2.5 seconds
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                5000);
+
+        // Note application network activity
+        NetworkStats networkStats = mockNetworkStats(10000, 1,
+                mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
+                mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
+
+        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
+
+        ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+                new int[]{100, 200, 300, 400, 500}, 600);
+        mockModemActivityInfo(mai);
+
+        mStatsRule.setTime(10_000, 10_000);
+
+        long energyUws = 10_000_000L * VOLTAGE_MV / 1000L;
+        when(mConsumedEnergyRetriever.getConsumedEnergyUws(
+                new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
+
+        when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
+        when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
+
+        PowerStats powerStats = collector.collectStats();
+
+        aggregatedStats.addPowerStats(powerStats, 10_000);
+
+        processor.finish(aggregatedStats);
+        return aggregatedStats;
+    }
+
     private int[] states(int... states) {
         return states;
     }
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 1d48975..2c03f9d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -72,6 +72,11 @@
         this(DEFAULT_CONFIG, clock, historyDirectory, handler, new PowerStatsUidResolver());
     }
 
+    MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, File historyDirectory) {
+        this(config, clock, historyDirectory, new Handler(Looper.getMainLooper()),
+                new PowerStatsUidResolver());
+    }
+
     MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, File historyDirectory,
             Handler handler, PowerStatsUidResolver powerStatsUidResolver) {
         super(config, clock, new MonotonicClock(0, clock), historyDirectory, handler,
@@ -137,13 +142,6 @@
         return MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS;
     }
 
-    public MockBatteryStatsImpl setBatteryStatsConfig(BatteryStatsConfig config) {
-        synchronized (this) {
-            mBatteryStatsConfig = config;
-        }
-        return this;
-    }
-
     public MockBatteryStatsImpl setNetworkStats(NetworkStats networkStats) {
         mNetworkStats = networkStats;
         return this;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
index 1b045c5..ae258cd3 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
@@ -29,8 +29,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
 import java.util.Arrays;
 
 @RunWith(AndroidJUnit4.class)
@@ -165,7 +163,7 @@
     }
 
     @Test
-    public void dump() {
+    public void test_toString() {
         MultiStateStats.Factory factory = makeFactory(true, true, false);
         MultiStateStats multiStateStats = factory.create();
         multiStateStats.setState(0 /* batteryState */, 0 /* off */, 1000);
@@ -175,13 +173,10 @@
         multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_BACKGROUND, 3000);
         multiStateStats.increment(new long[]{100, 200}, 5000);
 
-        StringWriter sw = new StringWriter();
-        PrintWriter pw = new PrintWriter(sw, true);
-        multiStateStats.dump(pw, Arrays::toString);
-        assertThat(sw.toString()).isEqualTo(
-                "plugged-in fg [25, 50]\n"
-                + "on-battery fg [25, 50]\n"
-                + "on-battery bg [50, 100]\n"
+        assertThat(multiStateStats.toString()).isEqualTo(
+                "(plugged-in fg) [25, 50]\n"
+                + "(on-battery fg) [25, 50]\n"
+                + "(on-battery bg) [50, 100]"
         );
     }
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
index dadcf3f..69d655b 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
@@ -98,6 +98,11 @@
                 }
 
                 @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
                 public PackageManager getPackageManager() {
                     return mPackageManager;
                 }
@@ -175,7 +180,7 @@
         aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
         aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 0);
 
-        MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+        MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector);
         collector.setEnabled(true);
 
         // Initial empty ModemActivityInfo.
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
index 8b1d423..a280cfe 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
@@ -138,6 +138,11 @@
         }
 
         @Override
+        public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+            return 0;
+        }
+
+        @Override
         public PackageManager getPackageManager() {
             return mPackageManager;
         }
@@ -304,16 +309,20 @@
         String dump = sw.toString();
         assertThat(dump).contains("duration=7500");
         assertThat(dump).contains(
-                "stats=[6000, 1000, 300, 200, 634, 945, " + ((64321 - 10000) * 1000 / 3500)
-                        + ", 0, 0]");
-        assertThat(dump).contains("UID 24: [6000, 3000, 60, 30, 400, 600, 0]");
-        assertThat(dump).contains("UID 42: [1000, 2000, 100, 200, 234, 345, 0]");
+                "rx: 6000 tx: 1000 idle: 300 scan: 200 basic-scan: 634 batched-scan: 945"
+                        + " energy: " + ((64321 - 10000) * 1000 / 3500));
+        assertThat(dump).contains(
+                "UID 24: rx-pkts: 60 rx-B: 6000 tx-pkts: 30 tx-B: 3000"
+                        + " scan: 400 batched-scan: 600");
+        assertThat(dump).contains(
+                "UID 42: rx-pkts: 100 rx-B: 1000 tx-pkts: 200 tx-B: 2000"
+                        + " scan: 234 batched-scan: 345");
     }
 
     private PowerStats collectPowerStats(boolean hasPowerReporting) {
         when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(hasPowerReporting);
 
-        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0);
+        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector);
         collector.setEnabled(true);
 
         when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI))
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
index 257a1a6..3ceaf35 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
@@ -142,6 +142,11 @@
                 }
 
                 @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
                 public PackageManager getPackageManager() {
                     return mPackageManager;
                 }
@@ -195,7 +200,7 @@
 
         PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
 
-        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0);
+        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector);
         collector.setEnabled(true);
 
         // Initial empty WifiActivityEnergyInfo.
@@ -307,7 +312,7 @@
 
         PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
 
-        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0);
+        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector);
         collector.setEnabled(true);
 
         // Initial empty WifiActivityEnergyInfo.
@@ -420,7 +425,7 @@
 
         PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
 
-        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0);
+        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector);
         collector.setEnabled(true);
 
         // Establish a baseline
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 27c522d..b56af87 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -25,6 +25,13 @@
                 value="/data/local/tmp/cts/content/broken_shortcut.xml" />
     </target_preparer>
 
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="force-skip-system-props" value="true" />
+        <option name="set-global-setting" key="verifier_engprod" value="1" />
+        <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+        <option name="restore-settings" value="true" />
+    </target_preparer>
+
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="install-arg" value="-t" />
diff --git a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
index 88ca029..ec78bce 100644
--- a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
@@ -112,7 +112,7 @@
         resources.addOverride(
                 com.android.internal.R.array.config_defaultPinnerServiceFiles, new String[0]);
         resources.addOverride(com.android.internal.R.bool.config_pinnerCameraApp, false);
-        resources.addOverride(com.android.internal.R.bool.config_pinnerHomeApp, false);
+        resources.addOverride(com.android.internal.R.integer.config_pinnerHomePinBytes, 0);
         resources.addOverride(com.android.internal.R.bool.config_pinnerAssistantApp, false);
 
         mFakeDeviceConfigInterface = new FakeDeviceConfigInterface();
@@ -242,7 +242,7 @@
     public void testPinHomeApp() throws Exception {
         // Enable HOME app pinning
         mContext.getOrCreateTestableResources()
-                .addOverride(com.android.internal.R.bool.config_pinnerHomeApp, true);
+                .addOverride(com.android.internal.R.integer.config_pinnerHomePinBytes, 1024);
         PinnerService pinnerService = new PinnerService(mContext, mInjector);
         pinnerService.onStart();
 
@@ -266,7 +266,7 @@
     public void testPinHomeAppOnBootCompleted() throws Exception {
         // Enable HOME app pinning
         mContext.getOrCreateTestableResources()
-                .addOverride(com.android.internal.R.bool.config_pinnerHomeApp, true);
+                .addOverride(com.android.internal.R.integer.config_pinnerHomePinBytes, 1024);
         PinnerService pinnerService = new PinnerService(mContext, mInjector);
         pinnerService.onStart();
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 3dc375c..9cd3186 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -368,24 +368,32 @@
     }
 
     @Test
-    public void testAuthenticate_throwsWhenUsingTestConfigurations() {
+    public void testAuthenticate_throwsWhenUsingTestApis() {
         final PromptInfo promptInfo = mock(PromptInfo.class);
-        when(promptInfo.containsPrivateApiConfigurations()).thenReturn(false);
-        when(promptInfo.containsTestConfigurations()).thenReturn(true);
+        when(promptInfo.requiresInternalPermission()).thenReturn(false);
+        when(promptInfo.requiresTestOrInternalPermission()).thenReturn(true);
 
-        testAuthenticate_throwsWhenUsingTestConfigurations(promptInfo);
+        testAuthenticate_throwsSecurityException(promptInfo);
     }
 
     @Test
     public void testAuthenticate_throwsWhenUsingPrivateApis() {
         final PromptInfo promptInfo = mock(PromptInfo.class);
-        when(promptInfo.containsPrivateApiConfigurations()).thenReturn(true);
-        when(promptInfo.containsTestConfigurations()).thenReturn(false);
+        when(promptInfo.requiresInternalPermission()).thenReturn(true);
+        when(promptInfo.requiresTestOrInternalPermission()).thenReturn(false);
 
-        testAuthenticate_throwsWhenUsingTestConfigurations(promptInfo);
+        testAuthenticate_throwsSecurityException(promptInfo);
     }
 
-    private void testAuthenticate_throwsWhenUsingTestConfigurations(PromptInfo promptInfo) {
+    @Test
+    public void testAuthenticate_throwsWhenUsingAdvancedApis() {
+        final PromptInfo promptInfo = mock(PromptInfo.class);
+        when(promptInfo.requiresAdvancedPermission()).thenReturn(true);
+
+        testAuthenticate_throwsSecurityException(promptInfo);
+    }
+
+    private void testAuthenticate_throwsSecurityException(PromptInfo promptInfo) {
         mAuthService = new AuthService(mContext, mInjector);
         mAuthService.onStart();
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index be5e262..c1ae852 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -24,8 +24,10 @@
 
 import static org.mockito.Mockito.spy;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.os.Looper;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
@@ -72,6 +74,11 @@
             protected void writeStringSystemProperty(String key, String value) {
                 // do nothing
             }
+
+            @Override
+            protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                // do nothing
+            }
         };
 
         Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 5be3c8e..a5f7bb1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -22,8 +22,10 @@
 
 import static org.mockito.Mockito.spy;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
 import android.os.Looper;
 import android.os.test.TestLooper;
@@ -84,6 +86,11 @@
                     protected Looper getServiceLooper() {
                         return mTestLooper.getLooper();
                     }
+
+                    @Override
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                        // do nothing
+                    }
                 };
 
         mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 7845c30..857ee1a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -22,8 +22,10 @@
 
 import static org.mockito.Mockito.spy;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
@@ -90,6 +92,11 @@
                     protected Looper getServiceLooper() {
                         return mTestLooper.getLooper();
                     }
+
+                    @Override
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                        // do nothing
+                    }
                 };
 
         Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
index 98789ac..6ace9f1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -33,8 +33,10 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.hdmi.DeviceFeatures;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
@@ -140,17 +142,8 @@
                         // do nothing
                     }
 
-                    /**
-                     * Override displayOsd to prevent it from broadcasting an intent, which
-                     * can trigger a SecurityException.
-                     */
                     @Override
-                    void displayOsd(int messageId) {
-                        // do nothing
-                    }
-
-                    @Override
-                    void displayOsd(int messageId, int extra) {
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
                         // do nothing
                     }
                 };
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index 9b65762..2dd593c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -18,6 +18,8 @@
 import static org.junit.Assert.assertEquals;
 
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
 import android.os.Looper;
@@ -103,6 +105,11 @@
                     protected Looper getServiceLooper() {
                         return mTestLooper.getLooper();
                     }
+
+                    @Override
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                        // do nothing
+                    }
                 };
         mHdmiCecLocalDeviceAudioSystem =
                 new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 922706e..e669e7c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -25,8 +25,10 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
@@ -90,6 +92,11 @@
             protected void writeStringSystemProperty(String key, String value) {
                 // do nothing
             }
+
+            @Override
+            protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                // do nothing
+            }
         };
 
         Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
index 68ef80f..29d20a6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
@@ -30,7 +30,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
@@ -123,6 +125,11 @@
                     boolean isPowerStandby() {
                         return false;
                     }
+
+                    @Override
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                        // do nothing
+                    }
                 };
 
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index 26b448a..d32b75b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -29,7 +29,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -132,6 +134,11 @@
                     boolean isPowerStandby() {
                         return false;
                     }
+
+                    @Override
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                        // do nothing
+                    }
                 };
 
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index a621055..c7574bd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -36,6 +36,7 @@
 
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -98,6 +99,8 @@
                 audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
         doNothing().when(mHdmiControlServiceSpy)
                 .writeStringSystemProperty(anyString(), anyString());
+        doNothing().when(mHdmiControlServiceSpy)
+                .sendBroadcastAsUser(any(Intent.class));
         doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
         HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
         doReturn(hdmiCecConfig).when(mHdmiControlServiceSpy).getHdmiCecConfig();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java
index 30ce961..e1e101f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java
@@ -19,6 +19,7 @@
 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_UNKNOWN;
 import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -30,6 +31,7 @@
 
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -90,6 +92,8 @@
                 audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
         doNothing().when(mHdmiControlServiceSpy)
                 .writeStringSystemProperty(anyString(), anyString());
+        doNothing().when(mHdmiControlServiceSpy)
+                .sendBroadcastAsUser(any(Intent.class));
         doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
 
         HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index 0870bac..7ed596e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -48,6 +48,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
 import android.os.Binder;
@@ -110,6 +111,8 @@
         doAnswer(__ -> mCecVersion).when(mHdmiControlServiceSpy).getCecVersion();
         doNothing().when(mHdmiControlServiceSpy)
                 .writeStringSystemProperty(anyString(), anyString());
+        doNothing().when(mHdmiControlServiceSpy)
+                .sendBroadcastAsUser(any(Intent.class));
         mHdmiControlServiceSpy.setDeviceConfig(new FakeDeviceConfigWrapper());
 
         mNativeWrapper = new FakeNativeWrapper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 5520897..5502de8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -26,7 +26,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -114,6 +116,11 @@
                             return defVal;
                     }
                 }
+
+                @Override
+                protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                    // do nothing
+                }
             };
 
         mHdmiControlService.getHdmiCecConfig().setIntValue(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 28da97c..8df7d54 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -28,7 +28,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -138,6 +140,11 @@
                     boolean canGoToStandby() {
                         return true;
                     }
+
+                    @Override
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                        // do nothing
+                    }
                 };
 
         mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 3dd8312..192be2a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -37,7 +37,9 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.tv.cec.V1_0.Result;
@@ -169,6 +171,11 @@
                     void wakeUp() {
                         mWakeupMessageReceived = true;
                     }
+
+                    @Override
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                        // do nothing
+                    }
                 };
         mHdmiControlService.setIoLooper(mTestLooper.getLooper());
         mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 4faeea5..b50684b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -26,6 +26,8 @@
 import static com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE;
+import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
+import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -41,7 +43,9 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -184,17 +188,8 @@
                         return mEarcBlocksArc;
                     }
 
-                    /**
-                     * Override displayOsd to prevent it from broadcasting an intent, which
-                     * can trigger a SecurityException.
-                    */
                     @Override
-                    void displayOsd(int messageId) {
-                        // do nothing
-                    }
-
-                    @Override
-                    void displayOsd(int messageId, int extra) {
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
                         // do nothing
                     }
                 };
@@ -1787,9 +1782,17 @@
         HdmiCecMessage activeSourceFromTv =
                 HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
 
-        mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
+        // Go to standby to invalidate the active source on the local device s.t. the
+        // RequestActiveSourceAction will start.
+        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
         mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
-        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+        mTestLooper.dispatchAll();
+
+        // Skip the LauncherX API timeout.
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
         mTestLooper.dispatchAll();
 
         assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1798,6 +1801,10 @@
         mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
         mTestLooper.dispatchAll();
 
+        // Assume there was a retry and the action did not finish earlier.
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
     }
 
@@ -1807,9 +1814,18 @@
                 HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
         HdmiCecMessage activeSourceFromTv =
                 HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
-        mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
+
+        // Go to standby to invalidate the active source on the local device s.t. the
+        // RequestActiveSourceAction will start.
+        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
         mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
-        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+        mTestLooper.dispatchAll();
+
+        // Skip the LauncherX API timeout.
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
         mTestLooper.dispatchAll();
 
         assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1834,8 +1850,18 @@
                 HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
         HdmiCecMessage activeSourceFromTv =
                 HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
-        mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
-        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+        // Go to standby to invalidate the active source on the local device s.t. the
+        // RequestActiveSourceAction will start.
+        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
+        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+        mTestLooper.dispatchAll();
+
+        // Skip the LauncherX API timeout.
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
         mTestLooper.dispatchAll();
 
         assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1854,8 +1880,16 @@
                 HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
         HdmiCecMessage activeSourceFromTv =
                 HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
-        mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
-        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+        // Go to standby to invalidate the active source on the local device s.t. the
+        // RequestActiveSourceAction will start.
+        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
+        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+        mTestLooper.dispatchAll();
+
         HdmiDeviceInfo playbackDevice = HdmiDeviceInfo.cecDeviceBuilder()
                 .setLogicalAddress(ADDR_PLAYBACK_1)
                 .setPhysicalAddress(0x1000)
@@ -1869,6 +1903,10 @@
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(playbackDevice);
         mTestLooper.dispatchAll();
 
+        // Skip the LauncherX API timeout.
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
+        mTestLooper.dispatchAll();
+
         assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
         mNativeWrapper.clearResultMessages();
         mHdmiCecLocalDeviceTv.deviceSelect(playbackDevice.getId(), null);
@@ -1881,6 +1919,41 @@
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
     }
 
+    @Test
+    public void onAddressAllocated_sendSourceChangingMessage_noRequestActiveSourceMessage() {
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
+        HdmiCecMessage activeSourceFromTv =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        HdmiCecMessage setStreamPathFromTv =
+                HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2000);
+
+        // Go to standby to invalidate the active source on the local device s.t. the
+        // RequestActiveSourceAction will start.
+        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
+        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+        mTestLooper.dispatchAll();
+
+        // Even if the device at the end of this path doesn't answer to this message, TV should not
+        // continue the RequestActiveSourceAction.
+        mHdmiControlService.sendCecCommand(setStreamPathFromTv);
+
+        // Skip the LauncherX API timeout.
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestActiveSource);
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        // Assume there was a retry and the action did not finish earlier.
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
+    }
 
     @Test
     public void newDeviceConnectedIfOnlyOneGiveOsdNameSent() {
@@ -1907,7 +1980,12 @@
         HdmiCecMessage activeSourceFromTv =
                 HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
 
-        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_WAKE_UP_MESSAGE);
+        // Go to standby to invalidate the active source on the local device s.t. the
+        // TV will send <Active Source> when it selects its internal source.
+        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
+        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
         mTestLooper.dispatchAll();
 
         mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
@@ -1937,6 +2015,8 @@
     public void handleStandby_fromActiveSource_standby() {
         mPowerManager.setInteractive(true);
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mTestLooper.dispatchAll();
+
         mHdmiControlService.setActiveSource(ADDR_PLAYBACK_1, 0x1000,
                 "HdmiCecLocalDeviceTvTest");
         mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index c002067..9412ee0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -21,8 +21,10 @@
 
 import static org.mockito.Mockito.spy;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -88,6 +90,11 @@
             boolean isPowerStandby() {
                 return false;
             }
+
+            @Override
+            protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                // do nothing
+            }
         };
         mHdmiControlService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index e1b66b5..126a658 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -52,6 +52,7 @@
 
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -121,6 +122,8 @@
                 audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
         doNothing().when(mHdmiControlServiceSpy)
                 .writeStringSystemProperty(anyString(), anyString());
+        doNothing().when(mHdmiControlServiceSpy)
+                .sendBroadcastAsUser(any(Intent.class));
 
         mMyLooper = mTestLooper.getLooper();
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 4641802..298ff46 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -26,8 +26,10 @@
 
 import static org.mockito.Mockito.spy;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
@@ -104,6 +106,11 @@
             protected void writeStringSystemProperty(String key, String value) {
                 // do nothing
             }
+
+            @Override
+            protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                // do nothing
+            }
         };
 
         Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 9f0a44c..1d4a72f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -25,8 +25,10 @@
 
 import static org.mockito.Mockito.spy;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -78,6 +80,11 @@
             protected void writeStringSystemProperty(String key, String value) {
                 // do nothing
             }
+
+            @Override
+            protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                // do nothing
+            }
         };
 
         Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
index 043db1e..cafe1e7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
@@ -21,7 +21,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.os.Looper;
@@ -115,6 +117,11 @@
                     boolean isPowerStandbyOrTransient() {
                         return false;
                     }
+
+                    @Override
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                        // do nothing
+                    }
                 };
 
         mHdmiControlService.setIoLooper(mMyLooper);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
index 061e1f9..864a182 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
@@ -21,7 +21,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
@@ -72,6 +74,11 @@
             protected void writeStringSystemProperty(String key, String value) {
                 // do nothing
             }
+
+            @Override
+            protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                // do nothing
+            }
         };
         mIsPowerStandby = false;
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
index b25ea2c..06709cd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
@@ -21,7 +21,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
 import android.os.Looper;
@@ -75,6 +77,11 @@
             boolean verifyPhysicalAddresses(HdmiCecMessage message) {
                 return true;
             }
+
+            @Override
+            protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                // do nothing
+            }
         };
 
         mMyLooper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
index f608c235..5163e29 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -29,7 +29,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
@@ -177,6 +179,11 @@
                     protected HdmiCecConfig getHdmiCecConfig() {
                         return hdmiCecConfig;
                     }
+
+                    @Override
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                        // do nothing
+                    }
                 };
 
         mHdmiControlService.setIoLooper(mMyLooper);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
index a73f4aa..e4297ef 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
@@ -24,12 +24,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.spy;
 
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.hdmi.DeviceFeatures;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
@@ -87,6 +89,8 @@
                 audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
         doNothing().when(mHdmiControlServiceSpy)
                 .writeStringSystemProperty(anyString(), anyString());
+        doNothing().when(mHdmiControlServiceSpy)
+                .sendBroadcastAsUser(any(Intent.class));
 
         mLooper = mTestLooper.getLooper();
         mHdmiControlServiceSpy.setIoLooper(mLooper);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 02bed22..4dcc6a4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -25,8 +25,10 @@
 
 import static org.mockito.Mockito.spy;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
 import android.os.Looper;
@@ -82,17 +84,8 @@
                 // do nothing
             }
 
-	    /**
-             * Override displayOsd to prevent it from broadcasting an intent, which
-             * can trigger a SecurityException.
-             */
             @Override
-            void displayOsd(int messageId) {
-                // do nothing
-            }
-
-            @Override
-            void displayOsd(int messageId, int extra) {
+            protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
                 // do nothing
             }
         };
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index df27e78..4aa074b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -23,7 +23,9 @@
 import static org.junit.Assert.assertTrue;
 
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
 import android.os.Looper;
@@ -143,6 +145,11 @@
                     protected boolean isStandbyMessageReceived() {
                         return mStandbyMessageReceived;
                     }
+
+                    @Override
+                    protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+                        // do nothing
+                    }
                 };
 
         Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
index 07fb9fc..570256b 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
@@ -19,9 +19,16 @@
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
 import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_RULE_DEFAULT;
+import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 import static android.util.DebugUtils.valueToString;
 
 import static org.junit.Assert.assertEquals;
@@ -51,7 +58,10 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.test.FakePermissionEnforcer;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 
 import androidx.test.filters.SmallTest;
@@ -62,6 +72,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -84,6 +95,9 @@
     @Mock private IBatteryStats.Stub mBatteryStatsService;
     @Mock private INetd.Stub mNetdService;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
     private static final int TEST_UID = 111;
 
     @NonNull
@@ -254,6 +268,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
     public void testMeteredNetworkRestrictions() throws RemoteException {
         // Make sure the mocked netd method returns true.
         doReturn(true).when(mNetdService).bandwidthEnableDataSaver(anyBoolean());
@@ -295,6 +310,69 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
+    public void testMeteredNetworkRestrictionsByAdminChain() {
+        mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+                FIREWALL_RULE_DENY);
+        verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+                FIREWALL_RULE_DENY);
+        assertTrue("Should be true since mobile data usage is restricted by admin chain",
+                mNMService.isNetworkRestricted(TEST_UID));
+
+        mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+                FIREWALL_RULE_DEFAULT);
+        verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+                FIREWALL_RULE_DEFAULT);
+        assertFalse("Should be false since mobile data usage is no longer restricted by admin",
+                mNMService.isNetworkRestricted(TEST_UID));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
+    public void testMeteredNetworkRestrictionsByUserChain() {
+        mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+                FIREWALL_RULE_DENY);
+        verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+                FIREWALL_RULE_DENY);
+        assertTrue("Should be true since mobile data usage is restricted by user chain",
+                mNMService.isNetworkRestricted(TEST_UID));
+
+        mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+                FIREWALL_RULE_DEFAULT);
+        verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+                FIREWALL_RULE_DEFAULT);
+        assertFalse("Should be false since mobile data usage is no longer restricted by user",
+                mNMService.isNetworkRestricted(TEST_UID));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
+    public void testDataSaverRestrictionsWithAllowChain() {
+        mNMService.setDataSaverModeEnabled(true);
+        verify(mCm).setDataSaverEnabled(true);
+
+        assertTrue("Should be true since data saver is on and the uid is not allowlisted",
+                mNMService.isNetworkRestricted(TEST_UID));
+
+        mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID, FIREWALL_RULE_ALLOW);
+        verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID, FIREWALL_RULE_ALLOW);
+        assertFalse("Should be false since data saver is on and the uid is allowlisted",
+                mNMService.isNetworkRestricted(TEST_UID));
+
+        // remove uid from allowlist and turn datasaver off again
+
+        mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID,
+                FIREWALL_RULE_DEFAULT);
+        verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID,
+                FIREWALL_RULE_DEFAULT);
+        mNMService.setDataSaverModeEnabled(false);
+        verify(mCm).setDataSaverEnabled(false);
+
+        assertFalse("Network should not be restricted when data saver is off",
+                mNMService.isNetworkRestricted(TEST_UID));
+    }
+
+    @Test
     public void testFirewallChains() {
         final ArrayMap<Integer, ArrayMap<Integer, Boolean>> expected = new ArrayMap<>();
         // Dozable chain
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 9fc46c5..2f3bca0 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -1786,10 +1786,10 @@
                     /* matchesInterruptionFilter= */ false,
                     /* visibilityOverride= */ 0,
                     /* suppressedVisualEffects= */ 0,
-                    mParentNotificationChannel.getImportance(),
+                    mNotificationChannel.getImportance(),
                     /* explanation= */ null,
                     /* overrideGroupKey= */ null,
-                    mParentNotificationChannel,
+                    mNotificationChannel,
                     /* overridePeople= */ null,
                     /* snoozeCriteria= */ null,
                     /* showBadge= */ true,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 4af20a9..70a0038 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -24,6 +24,8 @@
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 
@@ -52,6 +54,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.Manifest.permission;
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
@@ -67,6 +70,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
@@ -93,6 +97,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.R;
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
 import com.android.internal.config.sysui.TestableFlagResolver;
 import com.android.internal.logging.InstanceIdSequence;
@@ -188,6 +193,8 @@
         getContext().addMockSystemService(Vibrator.class, mVibrator);
         getContext().addMockSystemService(PackageManager.class, mPackageManager);
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false);
+        when(mPackageManager.checkPermission(eq(permission.RECEIVE_EMERGENCY_BROADCAST),
+                anyString())).thenReturn(PERMISSION_DENIED);
 
         when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
         when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
@@ -210,6 +217,16 @@
         verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt());
         assertTrue(mAccessibilityManager.isEnabled());
 
+        // Enable LED pulse setting by default
+        Settings.System.putInt(getContext().getContentResolver(),
+                Settings.System.NOTIFICATION_LIGHT_PULSE, 1);
+
+        Resources resources = spy(getContext().getResources());
+        when(resources.getBoolean(R.bool.config_useAttentionLight)).thenReturn(true);
+        when(resources.getBoolean(
+                com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(true);
+        when(getContext().getResources()).thenReturn(resources);
+
         // TODO (b/291907312): remove feature flag
         // Disable feature flags by default. Tests should enable as needed.
         mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS,
@@ -239,7 +256,6 @@
         mAttentionHelper.setKeyguardManager(mKeyguardManager);
         mAttentionHelper.setScreenOn(false);
         mAttentionHelper.setInCallStateOffHook(false);
-        mAttentionHelper.mNotificationPulseEnabled = true;
 
         if (Flags.crossAppPoliteNotifications()) {
             // Capture BroadcastReceiver for avalanche triggers
@@ -611,6 +627,14 @@
         verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
     }
 
+    private void verifyAttentionLights() {
+        verify(mLight, times(1)).pulse();
+    }
+
+    private void verifyNeverAttentionLights() {
+        verify(mLight, never()).pulse();
+    }
+
     //
     // Tests
     //
@@ -1524,7 +1548,10 @@
 
     @Test
     public void testLightsLightsOffGlobally() {
-        mAttentionHelper.mNotificationPulseEnabled = false;
+        Settings.System.putInt(getContext().getContentResolver(),
+                Settings.System.NOTIFICATION_LIGHT_PULSE, 0);
+        initAttentionHelper(mTestFlagResolver);
+
         NotificationRecord r = getLightsNotification();
         mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
         verifyNeverLights();
@@ -1533,6 +1560,44 @@
     }
 
     @Test
+    public void testLightsLightsResConfigDisabled() {
+        Resources resources = spy(getContext().getResources());
+        when(resources.getBoolean(
+                com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(false);
+        when(getContext().getResources()).thenReturn(resources);
+        initAttentionHelper(mTestFlagResolver);
+
+        NotificationRecord r = getLightsNotification();
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        verifyNeverLights();
+        assertFalse(r.isInterruptive());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
+    }
+
+    @Test
+    public void testLightsUseAttentionLight() {
+        NotificationRecord r = getLightsNotification();
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        verifyAttentionLights();
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
+    }
+
+    @Test
+    public void testLightsUseAttentionLightDisabled() {
+        Resources resources = spy(getContext().getResources());
+        when(resources.getBoolean(R.bool.config_useAttentionLight)).thenReturn(false);
+        when(resources.getBoolean(
+                com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(true);
+        when(getContext().getResources()).thenReturn(resources);
+        initAttentionHelper(mTestFlagResolver);
+
+        NotificationRecord r = getLightsNotification();
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        verifyNeverAttentionLights();
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
+    }
+
+    @Test
     public void testLightsDndIntercepted() {
         NotificationRecord r = getLightsNotification();
         r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_LIGHTS);
@@ -2303,6 +2368,72 @@
     }
 
     @Test
+    public void testBeepVolume_politeNotif_Avalanche_exemptEmergency() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS_ATTN_UPDATE);
+        TestableFlagResolver flagResolver = new TestableFlagResolver();
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+        initAttentionHelper(flagResolver);
+
+        // Trigger avalanche trigger intent
+        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intent.putExtra("state", false);
+        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+
+        NotificationRecord r = getBeepyNotification();
+
+        // Grant RECEIVE_EMERGENCY_BROADCAST to notification's package
+        when(mPackageManager.checkPermission(eq(permission.RECEIVE_EMERGENCY_BROADCAST),
+                eq(r.getSbn().getPackageName()))).thenReturn(PERMISSION_GRANTED);
+
+        // Should beep at 100% volume
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        verifyBeepVolume(1.0f);
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+        verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
+    }
+
+    @Test
+    public void testBeepVolume_politeNotif_exemptEmergency() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS_ATTN_UPDATE);
+        TestableFlagResolver flagResolver = new TestableFlagResolver();
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+        // NOTIFICATION_COOLDOWN_ALL setting is enabled
+        Settings.System.putInt(getContext().getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ALL, 1);
+        initAttentionHelper(flagResolver);
+
+        NotificationRecord r = getBeepyNotification();
+
+        // Grant RECEIVE_EMERGENCY_BROADCAST to notification's package
+        when(mPackageManager.checkPermission(eq(permission.RECEIVE_EMERGENCY_BROADCAST),
+                eq(r.getSbn().getPackageName()))).thenReturn(PERMISSION_GRANTED);
+
+        // set up internal state
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        Mockito.reset(mRingtonePlayer);
+
+        // update should beep at 100% volume
+        NotificationRecord r2 = getBeepyNotification();
+        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+        assertNotEquals(-1, r2.getLastAudiblyAlertedMs());
+        verifyBeepVolume(1.0f);
+
+        // 2nd update should beep at 100% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+        assertNotEquals(-1, r2.getLastAudiblyAlertedMs());
+        verifyBeepVolume(1.0f);
+
+        verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
+    }
+
+    @Test
     public void testBeepVolume_politeNotif_applyPerApp() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
         mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
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 2d672b8..e564ba6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -99,6 +99,7 @@
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_LOCKDOWN;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
@@ -112,6 +113,7 @@
 import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
 import static com.android.server.notification.Flags.FLAG_ALL_NOTIFS_NEED_TTL;
+import static com.android.server.notification.Flags.FLAG_REJECT_OLD_NOTIFICATIONS;
 import static com.android.server.notification.NotificationManagerService.BITMAP_DURATION;
 import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
 import static com.android.server.notification.NotificationManagerService.NOTIFICATION_TTL;
@@ -339,6 +341,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -909,7 +912,9 @@
         }
 
         mService.clearNotifications();
-        TestableLooper.get(this).processAllMessages();
+        if (mTestableLooper != null) {
+            mTestableLooper.processAllMessages();
+        }
 
         try {
             mService.onDestroy();
@@ -920,14 +925,16 @@
 
         InstrumentationRegistry.getInstrumentation()
                 .getUiAutomation().dropShellPermissionIdentity();
-        // Remove scheduled messages that would be processed when the test is already done, and
-        // could cause issues, for example, messages that remove/cancel shown toasts (this causes
-        // problematic interactions with mocks when they're no longer working as expected).
-        mWorkerHandler.removeCallbacksAndMessages(null);
+        if (mWorkerHandler != null) {
+            // Remove scheduled messages that would be processed when the test is already done, and
+            // could cause issues, for example, messages that remove/cancel shown toasts (this causes
+            // problematic interactions with mocks when they're no longer working as expected).
+            mWorkerHandler.removeCallbacksAndMessages(null);
+        }
 
-        if (TestableLooper.get(this) != null) {
+        if (mTestableLooper != null) {
             // Must remove static reference to this test object to prevent leak (b/261039202)
-            TestableLooper.remove(this);
+            mTestableLooper.remove(this);
         }
     }
 
@@ -1009,7 +1016,9 @@
     }
 
     public void waitForIdle() {
-        mTestableLooper.processAllMessages();
+        if (mTestableLooper != null) {
+            mTestableLooper.processAllMessages();
+        }
     }
 
     private void setUpPrefsForBubbles(String pkg, int uid, boolean globalEnabled,
@@ -1302,6 +1311,106 @@
         return nrSummary;
     }
 
+    private NotificationRecord createAndPostCallStyleNotification(String packageName,
+            UserHandle userHandle, String testName) throws Exception {
+        Person person = new Person.Builder().setName("caller").build();
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .setFlag(FLAG_USER_INITIATED_JOB, true)
+                .setStyle(Notification.CallStyle.forOngoingCall(person, mActivityIntent))
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        StatusBarNotification sbn = new StatusBarNotification(packageName, packageName, 1,
+                testName, mUid, 0, nb.build(), userHandle, null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mService.addEnqueuedNotification(r);
+        mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                r.getUid(), mPostNotificationTrackerFactory.newTracker(null)).run();
+        waitForIdle();
+
+        return mService.findNotificationLocked(
+                packageName, r.getSbn().getTag(), r.getSbn().getId(), r.getSbn().getUserId());
+    }
+
+    private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
+            throws RemoteException {
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1, testName, mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mBinderService.enqueueNotificationWithTag(mPkg, mPkg, sbn.getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+
+        return mService.findNotificationLocked(
+                mPkg, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+    }
+
+    private static <T extends Parcelable> T parcelAndUnparcel(T source,
+            Parcelable.Creator<T> creator) {
+        Parcel parcel = Parcel.obtain();
+        source.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        return creator.createFromParcel(parcel);
+    }
+
+    private PendingIntent createPendingIntent(String action) {
+        return PendingIntent.getActivity(mContext, 0,
+                new Intent(action).setPackage(mContext.getPackageName()),
+                PendingIntent.FLAG_MUTABLE);
+    }
+
+    private void allowTestPackageToToast() throws Exception {
+        assertWithMessage("toast queue").that(mService.mToastQueue).isEmpty();
+        mService.isSystemUid = false;
+        mService.isSystemAppId = false;
+        setToastRateIsWithinQuota(true);
+        setIfPackageHasPermissionToAvoidToastRateLimiting(TEST_PACKAGE, false);
+        // package is not suspended
+        when(mPackageManager.isPackageSuspendedForUser(TEST_PACKAGE, mUserId))
+                .thenReturn(false);
+    }
+
+    private boolean enqueueToast(String testPackage, ITransientNotification callback)
+            throws RemoteException {
+        return enqueueToast((INotificationManager) mService.mService, testPackage, new Binder(),
+                callback);
+    }
+
+    private boolean enqueueToast(INotificationManager service, String testPackage,
+            IBinder token, ITransientNotification callback) throws RemoteException {
+        return service.enqueueToast(testPackage, token, callback, TOAST_DURATION, /* isUiContext= */
+                true, DEFAULT_DISPLAY);
+    }
+
+    private boolean enqueueTextToast(String testPackage, CharSequence text) throws RemoteException {
+        return enqueueTextToast(testPackage, text, /* isUiContext= */ true, DEFAULT_DISPLAY);
+    }
+
+    private boolean enqueueTextToast(String testPackage, CharSequence text, boolean isUiContext,
+            int displayId) throws RemoteException {
+        return ((INotificationManager) mService.mService).enqueueTextToast(testPackage,
+                new Binder(), text, TOAST_DURATION, isUiContext, displayId,
+                /* textCallback= */ null);
+    }
+
+    private void mockIsVisibleBackgroundUsersSupported(boolean supported) {
+        when(mUm.isVisibleBackgroundUsersSupported()).thenReturn(supported);
+    }
+
+    private void mockIsUserVisible(int displayId, boolean visible) {
+        when(mUmInternal.isUserVisible(mUserId, displayId)).thenReturn(visible);
+    }
+
+    private void mockDisplayAssignedToUser(int displayId) {
+        when(mUmInternal.getMainDisplayAssignedToUser(mUserId)).thenReturn(displayId);
+    }
+
+    private void verifyToastShownForTestPackage(String text, int displayId) {
+        verify(mStatusBar).showToast(eq(mUid), eq(TEST_PACKAGE), any(), eq(text), any(),
+                eq(TOAST_DURATION), any(), eq(displayId));
+    }
+
     @Test
     @DisableFlags(FLAG_ALL_NOTIFS_NEED_TTL)
     public void testLimitTimeOutBroadcast() {
@@ -14069,11 +14178,12 @@
     public void enqueueUpdate_whenBelowMaxEnqueueRate_accepts() throws Exception {
         // Post the first version.
         Notification original = generateNotificationRecord(null).getNotification();
-        original.when = 111;
+        original.when = System.currentTimeMillis();
         mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 0, original, mUserId);
         waitForIdle();
         assertThat(mService.mNotificationList).hasSize(1);
-        assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111);
+        assertThat(mService.mNotificationList.get(0).getNotification().when)
+                .isEqualTo(original.when);
 
         reset(mUsageStats);
         when(mUsageStats.getAppEnqueueRate(eq(mPkg)))
@@ -14081,7 +14191,7 @@
 
         // Post the update.
         Notification update = generateNotificationRecord(null).getNotification();
-        update.when = 222;
+        update.when = System.currentTimeMillis() + 111;
         mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 0, update, mUserId);
         waitForIdle();
 
@@ -14090,18 +14200,19 @@
         verify(mUsageStats, never()).registerPostedByApp(any());
         verify(mUsageStats).registerUpdatedByApp(any(), any());
         assertThat(mService.mNotificationList).hasSize(1);
-        assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(222);
+        assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(update.when);
     }
 
     @Test
     public void enqueueUpdate_whenAboveMaxEnqueueRate_rejects() throws Exception {
         // Post the first version.
         Notification original = generateNotificationRecord(null).getNotification();
-        original.when = 111;
+        original.when = System.currentTimeMillis();
         mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 0, original, mUserId);
         waitForIdle();
         assertThat(mService.mNotificationList).hasSize(1);
-        assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111);
+        assertThat(mService.mNotificationList.get(0).getNotification().when)
+                .isEqualTo(original.when);
 
         reset(mUsageStats);
         when(mUsageStats.getAppEnqueueRate(eq(mPkg)))
@@ -14109,7 +14220,7 @@
 
         // Post the update.
         Notification update = generateNotificationRecord(null).getNotification();
-        update.when = 222;
+        update.when = System.currentTimeMillis() + 111;
         mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 0, update, mUserId);
         waitForIdle();
 
@@ -14118,7 +14229,8 @@
         verify(mUsageStats, never()).registerPostedByApp(any());
         verify(mUsageStats, never()).registerUpdatedByApp(any(), any());
         assertThat(mService.mNotificationList).hasSize(1);
-        assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); // old
+        assertThat(mService.mNotificationList.get(0).getNotification().when)
+                .isEqualTo(original.when); // old
     }
 
     @Test
@@ -15483,103 +15595,126 @@
         assertThat(n.getTimeoutAfter()).isEqualTo(20);
     }
 
-    private NotificationRecord createAndPostCallStyleNotification(String packageName,
-            UserHandle userHandle, String testName) throws Exception {
-        Person person = new Person.Builder().setName("caller").build();
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .setFlag(FLAG_USER_INITIATED_JOB, true)
-                .setStyle(Notification.CallStyle.forOngoingCall(person, mActivityIntent))
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-        StatusBarNotification sbn = new StatusBarNotification(packageName, packageName, 1,
-                testName, mUid, 0, nb.build(), userHandle, null, 0);
+    @Test
+    @EnableFlags(FLAG_REJECT_OLD_NOTIFICATIONS)
+    public void testRejectOldNotification_oldWhen() throws Exception {
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setWhen(System.currentTimeMillis() - Duration.ofDays(15).toMillis())
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
         NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mService.addEnqueuedNotification(r);
-        mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
-                r.getUid(), mPostNotificationTrackerFactory.newTracker(null)).run();
-        waitForIdle();
-
-        return mService.findNotificationLocked(
-                packageName, r.getSbn().getTag(), r.getSbn().getId(), r.getSbn().getUserId());
+        assertThat(mService.checkDisqualifyingFeatures(mUserId, mUid, 0, null, r, false, false))
+                .isFalse();
     }
 
-    private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
-            throws RemoteException {
-        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1, testName, mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+    @Test
+    @EnableFlags(FLAG_REJECT_OLD_NOTIFICATIONS)
+    public void testRejectOldNotification_mediumOldWhen() throws Exception {
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setWhen(System.currentTimeMillis() - Duration.ofDays(13).toMillis())
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(mPkg, mPkg, sbn.getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-
-        return mService.findNotificationLocked(
-                mPkg, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+        assertThat(mService.checkDisqualifyingFeatures(mUserId, mUid, 0, null, r, false, false))
+                .isTrue();
     }
 
-    private static <T extends Parcelable> T parcelAndUnparcel(T source,
-            Parcelable.Creator<T> creator) {
-        Parcel parcel = Parcel.obtain();
-        source.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        return creator.createFromParcel(parcel);
+    @Test
+    @EnableFlags(FLAG_REJECT_OLD_NOTIFICATIONS)
+    public void testRejectOldNotification_zeroWhen() throws Exception {
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setWhen(0)
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        assertThat(mService.checkDisqualifyingFeatures(mUserId, mUid, 0, null, r, false, false))
+                .isTrue();
     }
 
-    private PendingIntent createPendingIntent(String action) {
-        return PendingIntent.getActivity(mContext, 0,
-                new Intent(action).setPackage(mContext.getPackageName()),
-                PendingIntent.FLAG_MUTABLE);
+    @Test
+    public void testClearUIJFromUninstallingPackage() throws Exception {
+        NotificationRecord r =
+                generateNotificationRecord(mTestNotificationChannel, 0, mUserId, "bar");
+        mService.addNotification(r);
+
+        when(mPackageManagerClient.getPackageUidAsUser(anyString(), anyInt()))
+                .thenThrow(PackageManager.NameNotFoundException.class);
+        when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false);
+
+        mInternalService.cancelNotification(mPkg, mPkg, mUid, 0, r.getSbn().getTag(),
+                r.getSbn().getId(), mUserId);
+
+        // no exception
     }
 
-    private void allowTestPackageToToast() throws Exception {
-        assertWithMessage("toast queue").that(mService.mToastQueue).isEmpty();
+    @Test
+    public void testPostFromMissingPackage_throws() throws Exception {
+        NotificationRecord r =
+                generateNotificationRecord(mTestNotificationChannel, 0, mUserId, "bar");
+
+        when(mPackageManagerClient.getPackageUidAsUser(anyString(), anyInt()))
+                .thenThrow(PackageManager.NameNotFoundException.class);
+        when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false);
+
+        try {
+            mBinderService.enqueueNotificationWithTag(mPkg, mPkg, r.getSbn().getTag(),
+                    r.getSbn().getId(), r.getSbn().getNotification(),
+                    r.getSbn().getUserId());
+            fail("Allowed to post a notification for an absent package");
+        } catch (SecurityException e) {
+            // yay
+        }
+    }
+
+    @Test
+    public void testGetEffectsSuppressor_noSuppressor() throws Exception {
+        when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+        when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(true);
+        assertThat(mBinderService.getEffectsSuppressor()).isNull();
+    }
+
+    @Test
+    public void testGetEffectsSuppressor_suppressorSameApp() throws Exception {
+        when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
         mService.isSystemUid = false;
         mService.isSystemAppId = false;
-        setToastRateIsWithinQuota(true);
-        setIfPackageHasPermissionToAvoidToastRateLimiting(TEST_PACKAGE, false);
-        // package is not suspended
-        when(mPackageManager.isPackageSuspendedForUser(TEST_PACKAGE, mUserId))
-                .thenReturn(false);
+        mBinderService.requestHintsFromListener(mock(INotificationListener.class),
+                HINT_HOST_DISABLE_EFFECTS);
+        when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(true);
+        assertThat(mBinderService.getEffectsSuppressor()).isEqualTo(mListener.component);
     }
 
-    private boolean enqueueToast(String testPackage, ITransientNotification callback)
-            throws RemoteException {
-        return enqueueToast((INotificationManager) mService.mService, testPackage, new Binder(),
-                callback);
+    @Test
+    public void testGetEffectsSuppressor_suppressorDiffApp() throws Exception {
+        when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+        mService.isSystemUid = false;
+        mService.isSystemAppId = false;
+        mBinderService.requestHintsFromListener(mock(INotificationListener.class),
+                HINT_HOST_DISABLE_EFFECTS);
+        when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false);
+        assertThat(mBinderService.getEffectsSuppressor()).isEqualTo(null);
     }
 
-    private boolean enqueueToast(INotificationManager service, String testPackage,
-            IBinder token, ITransientNotification callback) throws RemoteException {
-        return service.enqueueToast(testPackage, token, callback, TOAST_DURATION, /* isUiContext= */
-                true, DEFAULT_DISPLAY);
-    }
-
-    private boolean enqueueTextToast(String testPackage, CharSequence text) throws RemoteException {
-        return enqueueTextToast(testPackage, text, /* isUiContext= */ true, DEFAULT_DISPLAY);
-    }
-
-    private boolean enqueueTextToast(String testPackage, CharSequence text, boolean isUiContext,
-            int displayId) throws RemoteException {
-        return ((INotificationManager) mService.mService).enqueueTextToast(testPackage,
-                new Binder(), text, TOAST_DURATION, isUiContext, displayId,
-                /* textCallback= */ null);
-    }
-
-    private void mockIsVisibleBackgroundUsersSupported(boolean supported) {
-        when(mUm.isVisibleBackgroundUsersSupported()).thenReturn(supported);
-    }
-
-    private void mockIsUserVisible(int displayId, boolean visible) {
-        when(mUmInternal.isUserVisible(mUserId, displayId)).thenReturn(visible);
-    }
-
-    private void mockDisplayAssignedToUser(int displayId) {
-        when(mUmInternal.getMainDisplayAssignedToUser(mUserId)).thenReturn(displayId);
-    }
-
-    private void verifyToastShownForTestPackage(String text, int displayId) {
-        verify(mStatusBar).showToast(eq(mUid), eq(TEST_PACKAGE), any(), eq(text), any(),
-                eq(TOAST_DURATION), any(), eq(displayId));
+    @Test
+    public void testGetEffectsSuppressor_suppressorDiffAppSystemCaller() throws Exception {
+        when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+        mService.isSystemUid = true;
+        mBinderService.requestHintsFromListener(mock(INotificationListener.class),
+                HINT_HOST_DISABLE_EFFECTS);
+        when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false);
+        assertThat(mBinderService.getEffectsSuppressor()).isEqualTo(mListener.component);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index 37e0818..5787780 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -24,6 +24,8 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
@@ -250,6 +252,7 @@
                 case ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN:
                 case ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN:
                 case ActivityOptions.KEY_TRANSIENT_LAUNCH:
+                case ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED:
                 case "android:activity.animationFinishedListener":
                     // KEY_ANIMATION_FINISHED_LISTENER
                 case "android:activity.animSpecs": // KEY_ANIM_SPECS
@@ -319,7 +322,7 @@
             Log.e("ActivityOptionsTests", "Unknown key " + key + " is found. "
                     + "Please review if the given bundle should be protected with permissions.");
         }
-        assertTrue(unknownKeys.isEmpty());
+        assertThat(unknownKeys).isEmpty();
     }
 
     public static class TrampolineActivity extends Activity {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
new file mode 100644
index 0000000..12ab3e1
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.servertransaction.RefreshCallbackItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.content.ComponentName;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link ActivityRefresher}.
+ *
+ * <p>Build/Install/Run:
+ *  atest WmTests:ActivityRefresherTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class ActivityRefresherTests extends WindowTestsBase {
+    private Handler mMockHandler;
+    private LetterboxConfiguration mLetterboxConfiguration;
+
+    private ActivityRecord mActivity;
+    private ActivityRefresher mActivityRefresher;
+
+    private ActivityRefresher.Evaluator mEvaluatorFalse =
+            (activity, newConfig, lastReportedConfig) -> false;
+
+    private ActivityRefresher.Evaluator mEvaluatorTrue =
+            (activity, newConfig, lastReportedConfig) -> true;
+
+    private final Configuration mNewConfig = new Configuration();
+    private final Configuration mOldConfig = new Configuration();
+
+    @Before
+    public void setUp() throws Exception {
+        mLetterboxConfiguration = mDisplayContent.mWmService.mLetterboxConfiguration;
+        spyOn(mLetterboxConfiguration);
+        when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled())
+                .thenReturn(true);
+        when(mLetterboxConfiguration.isCameraCompatRefreshEnabled())
+                .thenReturn(true);
+        when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
+                .thenReturn(true);
+
+        mMockHandler = mock(Handler.class);
+        when(mMockHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(
+                invocation -> {
+                    ((Runnable) invocation.getArgument(0)).run();
+                    return null;
+                });
+
+        mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mMockHandler);
+    }
+
+    @Test
+    public void testShouldRefreshActivity_refreshDisabled() throws Exception {
+        when(mLetterboxConfiguration.isCameraCompatRefreshEnabled())
+                .thenReturn(false);
+        configureActivityAndDisplay();
+        mActivityRefresher.addEvaluator(mEvaluatorTrue);
+
+        mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+        assertActivityRefreshRequested(/* refreshRequested= */ false);
+    }
+
+    @Test
+    public void testShouldRefreshActivity_refreshDisabledForActivity() throws Exception {
+        configureActivityAndDisplay();
+        when(mActivity.mLetterboxUiController.shouldRefreshActivityForCameraCompat())
+                .thenReturn(false);
+        mActivityRefresher.addEvaluator(mEvaluatorTrue);
+
+        mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+        assertActivityRefreshRequested(/* refreshRequested= */ false);
+    }
+
+    @Test
+    public void testShouldRefreshActivity_noRefreshTriggerers() throws Exception {
+        configureActivityAndDisplay();
+
+        mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+        assertActivityRefreshRequested(/* refreshRequested= */ false);
+    }
+
+    @Test
+    public void testShouldRefreshActivity_refreshTriggerersReturnFalse() throws Exception {
+        configureActivityAndDisplay();
+        mActivityRefresher.addEvaluator(mEvaluatorFalse);
+
+        mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+        assertActivityRefreshRequested(/* refreshRequested= */ false);
+    }
+
+    @Test
+    public void testShouldRefreshActivity_anyRefreshTriggerersReturnTrue() throws Exception {
+        configureActivityAndDisplay();
+        mActivityRefresher.addEvaluator(mEvaluatorFalse);
+        mActivityRefresher.addEvaluator(mEvaluatorTrue);
+
+        mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+        assertActivityRefreshRequested(/* refreshRequested= */ true);
+    }
+
+    @Test
+    public void testOnActivityConfigurationChanging_cycleThroughStopDisabled()
+            throws Exception {
+        mActivityRefresher.addEvaluator(mEvaluatorTrue);
+        when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
+                .thenReturn(false);
+        configureActivityAndDisplay();
+
+        mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+        assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
+    }
+
+    @Test
+    public void testOnActivityConfigurationChanging_cycleThroughPauseEnabledForApp()
+            throws Exception {
+        configureActivityAndDisplay();
+        mActivityRefresher.addEvaluator(mEvaluatorTrue);
+        doReturn(true).when(mActivity.mLetterboxUiController)
+                .shouldRefreshActivityViaPauseForCameraCompat();
+
+        mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+        assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
+    }
+
+    @Test
+    public void testOnActivityRefreshed_setIsRefreshRequestedToFalse() throws Exception {
+        configureActivityAndDisplay();
+        mActivityRefresher.addEvaluator(mEvaluatorTrue);
+        doReturn(true).when(mActivity.mLetterboxUiController)
+                .shouldRefreshActivityViaPauseForCameraCompat();
+
+        mActivityRefresher.onActivityRefreshed(mActivity);
+
+        assertActivityRefreshRequested(false);
+    }
+
+    private void assertActivityRefreshRequested(boolean refreshRequested) throws Exception {
+        assertActivityRefreshRequested(refreshRequested, /* cycleThroughStop*/ true);
+    }
+
+    private void assertActivityRefreshRequested(boolean refreshRequested,
+            boolean cycleThroughStop) throws Exception {
+        verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
+                .setIsRefreshRequested(true);
+
+        final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
+                cycleThroughStop ? ON_STOP : ON_PAUSE);
+        final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token,
+                /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
+
+        verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0))
+                .scheduleTransactionAndLifecycleItems(mActivity.app.getThread(),
+                        refreshCallbackItem, resumeActivityItem);
+    }
+
+    private void configureActivityAndDisplay() {
+        mActivity = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true)
+                .setDisplay(mDisplayContent)
+                // Set the component to be that of the test class in order to enable compat changes
+                .setComponent(ComponentName.createRelative(mContext,
+                        ActivityRefresherTests.class.getName()))
+                .setWindowingMode(WINDOWING_MODE_FREEFORM)
+                .build()
+                .getTopMostActivity();
+
+        spyOn(mActivity.mLetterboxUiController);
+        doReturn(true).when(
+                mActivity.mLetterboxUiController).shouldRefreshActivityForCameraCompat();
+
+        doReturn(true).when(mActivity).inFreeformWindowingMode();
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index 262ba8b..c76acd7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -91,6 +91,7 @@
     private CameraManager mMockCameraManager;
     private Handler mMockHandler;
     private LetterboxConfiguration mLetterboxConfiguration;
+    private ActivityRefresher mActivityRefresher;
 
     private DisplayRotationCompatPolicy mDisplayRotationCompatPolicy;
     private CameraManager.AvailabilityCallback mCameraAvailabilityCallback;
@@ -132,8 +133,9 @@
                 });
         CameraStateMonitor cameraStateMonitor =
                 new CameraStateMonitor(mDisplayContent, mMockHandler);
-        mDisplayRotationCompatPolicy =
-                new DisplayRotationCompatPolicy(mDisplayContent, mMockHandler, cameraStateMonitor);
+        mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mMockHandler);
+        mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(mDisplayContent,
+                cameraStateMonitor, mActivityRefresher);
 
         // Do not show the real toast.
         spyOn(mDisplayRotationCompatPolicy);
@@ -606,7 +608,7 @@
     private void assertActivityRefreshRequested(boolean refreshRequested,
                 boolean cycleThroughStop) throws Exception {
         verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
-                .setIsRefreshAfterRotationRequested(true);
+                .setIsRefreshRequested(true);
 
         final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
                 cycleThroughStop ? ON_STOP : ON_PAUSE);
@@ -628,7 +630,7 @@
 
     private void callOnActivityConfigurationChanging(
             ActivityRecord activity, boolean isDisplayRotationChanging) {
-        mDisplayRotationCompatPolicy.onActivityConfigurationChanging(activity,
+        mActivityRefresher.onActivityConfigurationChanging(activity,
                 /* newConfig */ createConfigurationWithDisplayRotation(ROTATION_0),
                 /* newConfig */ createConfigurationWithDisplayRotation(
                         isDisplayRotationChanging ? ROTATION_90 : ROTATION_0));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index 7380aec..d8d5729 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -65,7 +65,7 @@
         performSurfacePlacementAndWaitForWindowAnimator();
 
         mImeProvider.scheduleShowImePostLayout(appWin, ImeTracker.Token.empty());
-        assertTrue(mImeProvider.isReadyToShowIme());
+        assertTrue(mImeProvider.isScheduledAndReadyToShowIme());
     }
 
     /**
@@ -84,13 +84,13 @@
 
         // Schedule (without triggering) after everything is ready.
         mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
-        assertTrue(mImeProvider.isReadyToShowIme());
+        assertTrue(mImeProvider.isScheduledAndReadyToShowIme());
         assertFalse(mImeProvider.isImeShowing());
 
         // Manually trigger the show.
-        mImeProvider.checkShowImePostLayout();
-        // No longer ready as it was already shown.
-        assertFalse(mImeProvider.isReadyToShowIme());
+        mImeProvider.checkAndStartShowImePostLayout();
+        // No longer scheduled as it was already shown.
+        assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
         assertTrue(mImeProvider.isImeShowing());
     }
 
@@ -104,7 +104,7 @@
 
         // Schedule before anything is ready.
         mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
-        assertFalse(mImeProvider.isReadyToShowIme());
+        assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
         assertFalse(mImeProvider.isImeShowing());
 
         final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
@@ -115,8 +115,8 @@
         mDisplayContent.updateImeInputAndControlTarget(target);
         // Performing surface placement picks up the show scheduled above.
         performSurfacePlacementAndWaitForWindowAnimator();
-        // No longer ready as it was already shown.
-        assertFalse(mImeProvider.isReadyToShowIme());
+        // No longer scheduled as it was already shown.
+        assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
         assertTrue(mImeProvider.isImeShowing());
     }
 
@@ -137,19 +137,19 @@
 
         // Schedule before starting the afterPrepareSurfacesRunnable.
         mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
-        assertFalse(mImeProvider.isReadyToShowIme());
+        assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
         assertFalse(mImeProvider.isImeShowing());
 
         // This tries to pick up the show scheduled above, but must fail as the
         // afterPrepareSurfacesRunnable was not started yet.
         mDisplayContent.applySurfaceChangesTransaction();
-        assertFalse(mImeProvider.isReadyToShowIme());
+        assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
         assertFalse(mImeProvider.isImeShowing());
 
         // Starting the afterPrepareSurfacesRunnable picks up the show scheduled above.
         mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-        // No longer ready as it was already shown.
-        assertFalse(mImeProvider.isReadyToShowIme());
+        // No longer scheduled as it was already shown.
+        assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
         assertTrue(mImeProvider.isImeShowing());
     }
 
@@ -169,7 +169,7 @@
 
         // Schedule before surface placement.
         mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
-        assertFalse(mImeProvider.isReadyToShowIme());
+        assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
         assertFalse(mImeProvider.isImeShowing());
 
         // Performing surface placement picks up the show scheduled above, and succeeds.
@@ -177,8 +177,8 @@
         // applySurfaceChangesTransaction. Both of them try to trigger the show,
         // but only the second one can succeed, as it comes after onPostLayout.
         performSurfacePlacementAndWaitForWindowAnimator();
-        // No longer ready as it was already shown.
-        assertFalse(mImeProvider.isReadyToShowIme());
+        // No longer scheduled as it was already shown.
+        assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
         assertTrue(mImeProvider.isImeShowing());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 0e1a1af..c69faed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -353,6 +353,17 @@
     }
 
     @Test
+    public void testControlTargetChangedWhileProviderHasNoWindow() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final InsetsSourceProvider provider = getController().getOrCreateSourceProvider(
+                ID_STATUS_BAR, statusBars());
+        getController().onBarControlTargetChanged(app, null, null, null);
+        assertNull(getController().getControlsForDispatch(app));
+        provider.setWindowContainer(createWindow(null, TYPE_APPLICATION, "statusBar"), null, null);
+        assertNotNull(getController().getControlsForDispatch(app));
+    }
+
+    @Test
     public void testTransientVisibilityOfFixedRotationState() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index a60d243..6b17de4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -1594,7 +1594,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_REACHABILITY)
+    @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
     public void testAllowReachabilityForThinLetterboxWithFlagEnabled() {
         spyOn(mController);
         doReturn(true).when(mController).isVerticalThinLetterboxed();
@@ -1609,7 +1609,7 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_REACHABILITY)
+    @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
     public void testAllowReachabilityForThinLetterboxWithFlagDisabled() {
         spyOn(mController);
         doReturn(true).when(mController).isVerticalThinLetterboxed();
@@ -1623,6 +1623,12 @@
         assertTrue(mController.allowHorizontalReachabilityForThinLetterbox());
     }
 
+    @Test
+    public void testIsLetterboxEducationEnabled() {
+        mController.isLetterboxEducationEnabled();
+        verify(mLetterboxConfiguration).getIsEducationEnabled();
+    }
+
     private void mockThatProperty(String propertyName, boolean value) throws Exception {
         Property property = new Property(propertyName, /* value */ value, /* packageName */ "",
                 /* className */ "");
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 8fe45cb..76b4e005 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -99,18 +99,7 @@
 import java.util.stream.Collectors;
 
 /**
- * Subscription manager provides the mobile subscription information that are associated with the
- * calling user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U)
- * and below can see all subscriptions as it does today.
- *
- * <p>For example, if we have
- * <ul>
- *     <li> Subscription 1 associated with personal profile.
- *     <li> Subscription 2 associated with work profile.
- * </ul>
- * Then for SDK 35+, if the caller identity is personal profile, then
- * {@link #getActiveSubscriptionInfoList} will return subscription 1 only and vice versa.
- *
+ * Subscription manager provides the mobile subscription information.
  */
 @SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -1980,17 +1969,7 @@
     }
 
     /**
-     * Get the SubscriptionInfo(s) of the currently active SIM(s) associated with the current caller
-     * user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U)
-     * and below can see all subscriptions as it does today.
-     *
-     * <p>For example, if we have
-     * <ul>
-     *     <li> Subscription 1 associated with personal profile.
-     *     <li> Subscription 2 associated with work profile.
-     * </ul>
-     * Then for SDK 35+, if the caller identity is personal profile, then this will return
-     * subscription 1 only and vice versa.
+     * Get the SubscriptionInfo(s) of the currently active SIM(s).
      *
      * <p> Returned records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
      * {@link SubscriptionInfo#getSubscriptionId}. Beginning with Android SDK 35, this method will
@@ -2259,9 +2238,7 @@
     }
 
     /**
-     * Get the active subscription count associated with the current caller user profile for
-     * Android SDK 35(V) and above, while Android SDK 34(U) and below can see all subscriptions as
-     * it does today.
+     * Get the active subscription count.
      *
      * @return The current number of active subscriptions.
      *
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 03ba8fa..dbe4f27 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -13114,39 +13114,41 @@
     })
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public @Nullable ServiceState getServiceState(@IncludeLocationData int includeLocationData) {
-        return getServiceStateForSubscriber(getSubId(),
+        return getServiceStateForSlot(SubscriptionManager.getSlotIndex(getSubId()),
                 includeLocationData != INCLUDE_LOCATION_DATA_FINE,
                 includeLocationData == INCLUDE_LOCATION_DATA_NONE);
     }
 
     /**
-     * Returns the service state information on specified subscription. Callers require
-     * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
+     * Returns the service state information on specified SIM slot.
      *
-     * May return {@code null} when the subscription is inactive or when there was an error
+     * May return {@code null} when the {@code slotIndex} is invalid or when there was an error
      * communicating with the phone process.
+     *
+     * @param slotIndex of phone whose service state is returned
      * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
      * location related information which will be sent if the caller already possess
      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permission
      * @param renounceCoarseLocationAccess Set this to true if the caller would not like to
      * receive location related information which will be sent if the caller already possess
      * {@link Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the permissions.
+     * @return Service state on specified SIM slot.
      */
-    private ServiceState getServiceStateForSubscriber(int subId,
-            boolean renounceFineLocationAccess,
+    private ServiceState getServiceStateForSlot(int slotIndex, boolean renounceFineLocationAccess,
             boolean renounceCoarseLocationAccess) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.getServiceStateForSubscriber(subId, renounceFineLocationAccess,
-                        renounceCoarseLocationAccess, getOpPackageName(), getAttributionTag());
+                return service.getServiceStateForSlot(slotIndex,
+                        renounceFineLocationAccess, renounceCoarseLocationAccess,
+                        getOpPackageName(), getAttributionTag());
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
+            Log.e(TAG, "Error calling ITelephony#getServiceStateForSlot", e);
         } catch (NullPointerException e) {
             AnomalyReporter.reportAnomaly(
                     UUID.fromString("e2bed88e-def9-476e-bd71-3e572a8de6d1"),
-                    "getServiceStateForSubscriber " + subId + " NPE");
+                    "getServiceStateForSlot " + slotIndex + " NPE");
         }
         return null;
     }
@@ -13161,7 +13163,35 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public ServiceState getServiceStateForSubscriber(int subId) {
-        return getServiceStateForSubscriber(subId, false, false);
+        return getServiceStateForSlot(
+                SubscriptionManager.getSlotIndex(subId), false, false);
+    }
+
+    /**
+     * Returns the service state information on specified SIM slot.
+     *
+     * If you want continuous updates of service state info, register a {@link TelephonyCallback}
+     * that implements {@link TelephonyCallback.ServiceStateListener} through
+     * {@link #registerTelephonyCallback}.
+     *
+     * May return {@code null} when the {@code slotIndex} is invalid or when there was an error
+     * communicating with the phone process
+     *
+     * See {@link #getActiveModemCount()} to get the total number of slots
+     * that are active on the device.
+     *
+     * @param slotIndex of phone whose service state is returned
+     * @return ServiceState on specified SIM slot.
+     *
+     * @hide
+     */
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @Nullable ServiceState getServiceStateForSlot(int slotIndex) {
+        return getServiceStateForSlot(slotIndex, false, false);
     }
 
     /**
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 0bb5fd5..47f53f3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -1015,13 +1015,27 @@
      * @hide
      */
     public static final int DATAGRAM_TYPE_KEEP_ALIVE = 3;
+    /**
+     * Datagram type indicating that the datagram to be sent or received is of type SOS message and
+     * is the last message to emergency service provider indicating still needs help.
+     * @hide
+     */
+    public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP = 4;
+    /**
+     * Datagram type indicating that the datagram to be sent or received is of type SOS message and
+     * is the last message to emergency service provider indicating no more help is needed.
+     * @hide
+     */
+    public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED = 5;
 
     /** @hide */
     @IntDef(prefix = "DATAGRAM_TYPE_", value = {
             DATAGRAM_TYPE_UNKNOWN,
             DATAGRAM_TYPE_SOS_MESSAGE,
             DATAGRAM_TYPE_LOCATION_SHARING,
-            DATAGRAM_TYPE_KEEP_ALIVE
+            DATAGRAM_TYPE_KEEP_ALIVE,
+            DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP,
+            DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DatagramType {}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d4da736..65de7e4 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1399,19 +1399,18 @@
     oneway void requestModemActivityInfo(in ResultReceiver result);
 
     /**
-     * Get the service state on specified subscription
-     * @param subId Subscription id
+     * Get the service state on specified SIM slot.
+     * @param slotIndex of phone whose service state is returned
      * @param renounceFineLocationAccess Set this to true if the caller would not like to
      * receive fine location related information
      * @param renounceCoarseLocationAccess Set this to true if the caller would not like to
      * receive coarse location related information
      * @param callingPackage The package making the call
      * @param callingFeatureId The feature in the package
-     * @return Service state on specified subscription.
+     * @return Service state on specified SIM slot.
      */
-    ServiceState getServiceStateForSubscriber(int subId, boolean renounceFineLocationAccess,
-            boolean renounceCoarseLocationAccess,
-            String callingPackage, String callingFeatureId);
+    ServiceState getServiceStateForSlot(int slotIndex, boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess, String callingPackage, String callingFeatureId);
 
     /**
      * Returns the URI for the per-account voicemail ringtone set in Phone settings.
diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml
index 6f8f008..955b43a 100644
--- a/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml
@@ -19,7 +19,7 @@
           xmlns:tools="http://schemas.android.com/tools"
           package="com.android.server.wm.flicker">
 
-    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="35"/>
     <!-- Read and write traces from external storage -->
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@@ -46,6 +46,8 @@
     <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
     <!-- Allow the test to connect to perfetto trace processor -->
     <uses-permission android:name="android.permission.INTERNET"/>
+    <!-- Allow to query for the Launcher TestInfo on SDK 30+ -->
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
     <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
     <application android:requestLegacyExternalStorage="true"
                  android:networkSecurityConfig="@xml/network_security_config"
diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
index 1dc1037..82de070 100644
--- a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
index cf4edd5..67825d2 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
@@ -43,6 +43,7 @@
  *
  * To run this test: `atest FlickerTestsOther:OpenTrampolineActivityTest`
  */
+@FlakyTest(bugId = 341209752)
 @RequiresDevice
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -168,7 +169,6 @@
         }
     }
 
-    @FlakyTest(bugId = 290736037)
     /** Main activity should go from fullscreen to being a split with secondary activity. */
     @Test
     fun mainActivityLayerGoesFromFullscreenToSplit() {
@@ -203,7 +203,6 @@
         }
     }
 
-    @FlakyTest(bugId = 288591571)
     @Test
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         super.visibleLayersShownMoreThanOneConsecutiveEntry()
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index bc3696b..eed9225 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -205,7 +205,8 @@
                         it.visibleRegion(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
                     val secondaryVisibleRegion =
                         it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
-                    overlayVisibleRegion.coversExactly(secondaryVisibleRegion.region)
+                    // TODO(b/340992001): replace coverAtLeast with coverExactly
+                    overlayVisibleRegion.coversAtLeast(secondaryVisibleRegion.region)
                 }
                 .then()
                 .isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index fb92583..379b45c 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -60,14 +60,16 @@
             testApp.launchViaIntent(wmHelper)
             testApp.launchSecondaryActivity(wmHelper)
             secondaryApp.launchViaIntent(wmHelper)
-            tapl.goHome()
-            wmHelper
-                .StateSyncBuilder()
-                .withAppTransitionIdle()
-                .withHomeActivityVisible()
-                .waitForAndVerify()
             startDisplayBounds =
                 wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
+
+            // Record the displayBounds before `goHome()` in case the launcher is fixed-portrait.
+            tapl.goHome()
+            wmHelper
+                    .StateSyncBuilder()
+                    .withAppTransitionIdle()
+                    .withHomeActivityVisible()
+                    .waitForAndVerify()
         }
         transitions {
             SplitScreenUtils.enterSplit(
@@ -138,10 +140,6 @@
             check { "ActivityEmbeddingSplitHeight" }
                 .that(leftAELayerRegion.region.bounds.height())
                 .isEqual(rightAELayerRegion.region.bounds.height())
-            check { "SystemSplitHeight" }
-                .that(rightAELayerRegion.region.bounds.height())
-                .isEqual(secondaryAppLayerRegion.region.bounds.height())
-            // TODO(b/292283182): Remove this special case handling.
             check { "ActivityEmbeddingSplitWidth" }
                 .that(
                     abs(
@@ -150,14 +148,6 @@
                     )
                 )
                 .isLower(2)
-            check { "SystemSplitWidth" }
-                .that(
-                    abs(
-                        secondaryAppLayerRegion.region.bounds.width() -
-                            2 * rightAELayerRegion.region.bounds.width()
-                    )
-                )
-                .isLower(2)
         }
     }
 
@@ -170,15 +160,9 @@
                 visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
             val rightAEWindowRegion =
                 visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
-            // There's no window for the divider bar.
-            val secondaryAppLayerRegion =
-                visibleRegion(ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
             check { "ActivityEmbeddingSplitHeight" }
                 .that(leftAEWindowRegion.region.bounds.height())
                 .isEqual(rightAEWindowRegion.region.bounds.height())
-            check { "SystemSplitHeight" }
-                .that(rightAEWindowRegion.region.bounds.height())
-                .isEqual(secondaryAppLayerRegion.region.bounds.height())
             check { "ActivityEmbeddingSplitWidth" }
                 .that(
                     abs(
@@ -187,14 +171,6 @@
                     )
                 )
                 .isLower(2)
-            check { "SystemSplitWidth" }
-                .that(
-                    abs(
-                        secondaryAppLayerRegion.region.bounds.width() -
-                            2 * rightAEWindowRegion.region.bounds.width()
-                    )
-                )
-                .isLower(2)
         }
     }
 
diff --git a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
index 57a58c8..4ffb11a 100644
--- a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
index 2cb86e0..0fa4d07 100644
--- a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
index 2cf85fa..4d9fefb 100644
--- a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml
index b93e1be..b879c54 100644
--- a/tests/FlickerTests/IME/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml
@@ -82,6 +82,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/IME/OWNERS b/tests/FlickerTests/IME/OWNERS
index ae1098d..e3a2e67 100644
--- a/tests/FlickerTests/IME/OWNERS
+++ b/tests/FlickerTests/IME/OWNERS
@@ -1,3 +1,3 @@
 # ime
 # Bug component: 34867
-include /services/core/java/com/android/server/inputmethod/OWNERS
+file:/services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index da8368f..2b6ddcb 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -32,6 +32,9 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
+/**
+ * To run this test: `atest FlickerTestsIme1:CloseImeOnDismissPopupDialogTest`
+ */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
index 2f3ec63..0344197 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
@@ -33,8 +33,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window closing to home transitions. To run this test: `atest
- * FlickerTests:CloseImeWindowToHomeTest`
+ * Test IME window closing to home transitions.
+ * To run this test: `atest FlickerTestsIme1:CloseImeOnGoHomeTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
index 8821b69..fde1373 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
@@ -42,7 +42,7 @@
  *
  * More details on b/190352379
  *
- * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
+ * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartOnGoHomeTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
index d75eba6..dc50135 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -42,7 +42,7 @@
  *
  * More details on b/190352379
  *
- * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
+ * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartToAppOnPressBackTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index 41d9e30..dc2bd1b 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -34,8 +34,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window closing back to app window transitions. To run this test: `atest
- * FlickerTests:CloseImeWindowToAppTest`
+ * Test IME window closing back to app window transitions.
+ * To run this test: `atest FlickerTestsIme1:CloseImeToAppOnPressBackTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index 0e7fb79..05771e8 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -40,7 +40,7 @@
  * Unlike {@link OpenImeWindowTest} testing IME window opening transitions, this test also verify
  * there is no flickering when back to the simple activity without requesting IME to show.
  *
- * To run this test: `atest FlickerTests:OpenImeWindowAndCloseTest`
+ * To run this test: `atest FlickerTestsIme1:CloseImeToHomeOnFinishActivityTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
index 47a7e1b..336fe6f 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -36,8 +36,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window shown on the app with fixing portrait orientation. To run this test: `atest
- * FlickerTests:OpenImeWindowToFixedPortraitAppTest`
+ * Test IME window shown on the app with fixing portrait orientation.
+ * To run this test: `atest FlickerTestsIme2:OpenImeWindowToFixedPortraitAppTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index 48ec4d1..b8f11dc 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -38,8 +38,9 @@
 
 /**
  * Test IME window layer will become visible when switching from the fixed orientation activity
- * (e.g. Launcher activity). To run this test: `atest
- * FlickerTests:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest`
+ * (e.g. Launcher activity).
+ * To run this test:
+ * `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
index 03f3a68..34a7085 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
@@ -33,7 +33,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window opening transitions. To run this test: `atest FlickerTests:ReOpenImeWindowTest`
+ * Test IME window opening transitions.
+ * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromOverviewTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
index 7b62c89..7c72c31 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -35,8 +35,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME windows switching with 2-Buttons or gestural navigation. To run this test: `atest
- * FlickerTests:SwitchImeWindowsFromGestureNavTest`
+ * Test IME windows switching with 2-Buttons or gestural navigation.
+ * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
index 53bfb4e..fe5320c 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -36,7 +36,7 @@
 /**
  * Launch an app that automatically displays the IME
  *
- * To run this test: `atest FlickerTests:LaunchAppShowImeOnStartTest`
+ * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
index d22bdcf..92b6b93 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
@@ -35,8 +35,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window closing on lock and opening on screen unlock. To run this test: `atest
- * FlickerTests:CloseImeWindowToHomeTest`
+ * Test IME window closing on lock and opening on screen unlock.
+ * To run this test: `atest FlickerTestsIme2:ShowImeOnUnlockScreenTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
index 12290af..9eaf998 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
@@ -31,7 +31,10 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-/** Test IME window opening transitions. To run this test: `atest FlickerTests:OpenImeWindowTest` */
+/**
+ * Test IME window opening transitions.
+ * To run this test: `atest FlickerTestsIme2:ShowImeWhenFocusingOnInputFieldTest`
+ */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index 0948351..7186a2c 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -41,7 +41,7 @@
 
 /**
  * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity.
- * To run this test: `atest FlickerTests:LaunchAppShowImeAndDialogThemeAppTest`
+ * To run this test: `atest FlickerTestsIme2:ShowImeWhileDismissingThemedPopupDialogTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index 7aa525f..c96c760 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -37,8 +37,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window layer will be associated with the app task when going to the overview screen. To
- * run this test: `atest FlickerTests:OpenImeWindowToOverViewTest`
+ * Test IME window layer will be associated with the app task when going to the overview screen.
+ * To run this test: `atest FlickerTestsIme2:ShowImeWhileEnteringOverviewTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/Notification/AndroidTestTemplate.xml b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
index 9c6a17d3..04b312a 100644
--- a/tests/FlickerTests/Notification/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
index ffaeead..8c9ab9a 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
@@ -40,7 +40,7 @@
  *
  * This test assumes the device doesn't have AOD enabled
  *
- * To run this test: `atest FlickerTests:OpenAppFromLockNotificationCold`
+ * To run this test: `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationColdTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
index 6e67e19..e595100a 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
@@ -40,7 +40,7 @@
  *
  * This test assumes the device doesn't have AOD enabled
  *
- * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWarm`
+ * To run this test: `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationWarmTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
index f1df8a6..fbe1d34 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
@@ -40,7 +40,8 @@
  *
  * This test assumes the device doesn't have AOD enabled
  *
- * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWithLockOverlayApp`
+ * To run this test:
+ * `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationWithOverlayAppTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
index b6d09d0..c8ca644 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
@@ -36,7 +36,7 @@
 /**
  * Test cold launching an app from a notification.
  *
- * To run this test: `atest FlickerTests:OpenAppFromNotificationCold`
+ * To run this test: `atest FlickerTestsNotification:OpenAppFromNotificationColdTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
index 1e607bf..c29e71c 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
@@ -47,7 +47,7 @@
 /**
  * Test cold launching an app from a notification.
  *
- * To run this test: `atest FlickerTests:OpenAppFromNotificationWarm`
+ * To run this test: `atest FlickerTestsNotification:OpenAppFromNotificationWarmTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
index ecbed28..8acdabc 100644
--- a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
index 1eacdfd..91ece21 100644
--- a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
                 value="trace_config.textproto"
         />
         <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
     </test>
     <!-- Needed for pulling the collected trace config on to the host -->
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 9198ae1..3e500d9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -18,7 +18,10 @@
           package="com.android.server.wm.flicker.testapp">
 
     <uses-sdk android:minSdkVersion="29"
-              android:targetSdkVersion="29"/>
+              android:targetSdkVersion="35"/>
+
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+
     <application android:allowBackup="false"
                  android:supportsRtl="true">
         <uses-library android:name="androidx.window.extensions" android:required="false"/>
@@ -107,7 +110,7 @@
                   android:immersive="true"
                   android:resizeableActivity="true"
                   android:screenOrientation="portrait"
-                  android:theme="@android:style/Theme.NoTitleBar"
+                  android:theme="@style/OptOutEdgeToEdge.NoTitleBar"
                   android:configChanges="screenSize"
                   android:label="PortraitImmersiveActivity"
                   android:exported="true">
@@ -119,7 +122,7 @@
         <activity android:name=".LaunchTransparentActivity"
                   android:resizeableActivity="false"
                   android:screenOrientation="portrait"
-                  android:theme="@android:style/Theme"
+                  android:theme="@style/OptOutEdgeToEdge"
                   android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchTransparentActivity"
                   android:label="LaunchTransparentActivity"
                   android:exported="true">
@@ -273,7 +276,7 @@
                   android:exported="true"
                   android:label="MailActivity"
                   android:taskAffinity="com.android.server.wm.flicker.testapp.MailActivity"
-                  android:theme="@style/Theme.AppCompat.Light">
+                  android:theme="@style/OptOutEdgeToEdge.AppCompatTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
@@ -282,7 +285,7 @@
         <activity android:name=".GameActivity"
                   android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity"
                   android:immersive="true"
-                  android:theme="@android:style/Theme.NoTitleBar"
+                  android:theme="@style/OptOutEdgeToEdge.NoTitleBar"
                   android:configChanges="screenSize"
                   android:label="GameActivity"
                   android:exported="true">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml
index 86c21906..917aec1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml
@@ -14,66 +14,71 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 -->
-<LinearLayout
+<ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
     android:background="@android:color/holo_orange_light">
 
-    <Button
-        android:id="@+id/launch_secondary_activity_button"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:onClick="launchSecondaryActivity"
-        android:tag="LEFT_TO_RIGHT"
-        android:text="Launch Secondary Activity" />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
 
-    <Button
-        android:id="@+id/launch_secondary_activity_rtl_button"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:onClick="launchSecondaryActivity"
-        android:tag="RIGHT_TO_LEFT"
-        android:text="Launch Secondary Activity in RTL" />
+        <Button
+            android:id="@+id/launch_secondary_activity_button"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:onClick="launchSecondaryActivity"
+            android:tag="LEFT_TO_RIGHT"
+            android:text="Launch Secondary Activity" />
 
-    <Button
-        android:id="@+id/launch_secondary_activity_horizontally_button"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:onClick="launchSecondaryActivity"
-        android:tag="BOTTOM_TO_TOP"
-        android:text="Launch Secondary Activity Horizontally" />
+        <Button
+            android:id="@+id/launch_secondary_activity_rtl_button"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:onClick="launchSecondaryActivity"
+            android:tag="RIGHT_TO_LEFT"
+            android:text="Launch Secondary Activity in RTL" />
 
-    <Button
-        android:id="@+id/launch_placeholder_split_button"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:onClick="launchPlaceholderSplit"
-        android:tag="LEFT_TO_RIGHT"
-        android:text="Launch Placeholder Split" />
+        <Button
+            android:id="@+id/launch_secondary_activity_horizontally_button"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:onClick="launchSecondaryActivity"
+            android:tag="BOTTOM_TO_TOP"
+            android:text="Launch Secondary Activity Horizontally" />
 
-    <Button
-        android:id="@+id/launch_always_expand_activity_button"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:onClick="launchAlwaysExpandActivity"
-        android:text="Launch Always Expand Activity" />
+        <Button
+            android:id="@+id/launch_placeholder_split_button"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:onClick="launchPlaceholderSplit"
+            android:tag="LEFT_TO_RIGHT"
+            android:text="Launch Placeholder Split" />
 
-    <Button
-        android:id="@+id/launch_placeholder_split_rtl_button"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:onClick="launchPlaceholderSplit"
-        android:tag="RIGHT_TO_LEFT"
-        android:text="Launch Placeholder Split in RTL" />
+        <Button
+            android:id="@+id/launch_always_expand_activity_button"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:onClick="launchAlwaysExpandActivity"
+            android:text="Launch Always Expand Activity" />
 
-    <Button
-        android:id="@+id/launch_trampoline_button"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:onClick="launchTrampolineActivity"
-        android:tag="LEFT_TO_RIGHT"
-        android:text="Launch Trampoline Activity" />
+        <Button
+            android:id="@+id/launch_placeholder_split_rtl_button"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:onClick="launchPlaceholderSplit"
+            android:tag="RIGHT_TO_LEFT"
+            android:text="Launch Placeholder Split in RTL" />
 
-</LinearLayout>
+        <Button
+            android:id="@+id/launch_trampoline_button"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:onClick="launchTrampolineActivity"
+            android:tag="LEFT_TO_RIGHT"
+            android:text="Launch Trampoline Activity" />
+
+    </LinearLayout>
+</ScrollView>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 9b742d9..47d1137 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -16,7 +16,19 @@
   -->
 
 <resources>
-    <style name="DefaultTheme" parent="@android:style/Theme.DeviceDefault">
+    <style name="OptOutEdgeToEdge" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+    </style>
+
+    <style name="OptOutEdgeToEdge.NoTitleBar" parent="@android:style/Theme.NoTitleBar">
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+    </style>
+
+    <style name="OptOutEdgeToEdge.AppCompatTheme" parent="@style/Theme.AppCompat.Light">
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+    </style>
+
+    <style name="DefaultTheme" parent="@style/OptOutEdgeToEdge">
         <item name="android:windowBackground">@android:color/darker_gray</item>
     </style>
 
@@ -32,7 +44,7 @@
         <item name="android:windowLayoutInDisplayCutoutMode">never</item>
     </style>
 
-    <style name="DialogTheme" parent="@android:style/Theme.DeviceDefault">
+    <style name="DialogTheme" parent="@style/OptOutEdgeToEdge">
         <item name="android:windowAnimationStyle">@null</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@null</item>
@@ -43,18 +55,18 @@
         <item name="android:windowSoftInputMode">stateUnchanged</item>
     </style>
 
-    <style name="TransparentTheme" parent="@android:style/Theme.DeviceDefault">
+    <style name="TransparentTheme" parent="@style/OptOutEdgeToEdge">
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:backgroundDimEnabled">false</item>
     </style>
 
-    <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
+    <style name="no_starting_window" parent="@style/OptOutEdgeToEdge">
         <item name="android:windowDisablePreview">true</item>
     </style>
 
-    <style name="SplashscreenAppTheme" parent="@android:style/Theme.DeviceDefault">
+    <style name="SplashscreenAppTheme" parent="@style/OptOutEdgeToEdge">
         <!-- Splashscreen Attributes -->
         <item name="android:windowSplashScreenAnimatedIcon">@drawable/avd_anim</item>
         <!-- Here we want to match the duration of our AVD -->
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
index c92b82b..a86ba5f 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
@@ -125,7 +125,7 @@
                 .setContentTitle("BubbleChat")
                 .setContentIntent(PendingIntent.getActivity(mContext, 0,
                         new Intent(mContext, LaunchBubbleActivity.class),
-                        PendingIntent.FLAG_UPDATE_CURRENT))
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE))
                 .setStyle(new Notification.MessagingStyle(chatBot)
                         .setConversationTitle("BubbleChat")
                         .addMessage("BubbleChat",
@@ -140,7 +140,7 @@
         Intent target = new Intent(mContext, BubbleActivity.class);
         target.putExtra(EXTRA_BUBBLE_NOTIF_ID, info.id);
         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, info.id, target,
-                PendingIntent.FLAG_UPDATE_CURRENT);
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
 
         return new Notification.BubbleMetadata.Builder()
                 .setIntent(bubbleIntent)
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
index dea3444..37332c9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
@@ -17,6 +17,9 @@
 package com.android.server.wm.flicker.testapp;
 
 
+import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.app.Activity;
 import android.app.Person;
 import android.content.Context;
@@ -24,6 +27,7 @@
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.graphics.drawable.Icon;
+import android.os.Build;
 import android.os.Bundle;
 import android.view.View;
 
@@ -36,6 +40,13 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+                && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
+            // POST_NOTIFICATIONS permission required for notification post sdk 33.
+            requestPermissions(new String[] { POST_NOTIFICATIONS }, 0);
+        }
+
         addInboxShortcut(getApplicationContext());
         mBubbleHelper = BubbleHelper.getInstance(this);
         setContentView(R.layout.activity_main);
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
index a4dd575..d6427ab 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm.flicker.testapp;
 
+import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.app.Activity;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -23,6 +26,7 @@
 import android.app.PendingIntent;
 import android.app.TaskStackBuilder;
 import android.content.Intent;
+import android.os.Build;
 import android.os.Bundle;
 import android.view.WindowManager;
 import android.widget.Button;
@@ -34,6 +38,13 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+                && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
+            // POST_NOTIFICATIONS permission required for notification post sdk 33.
+            requestPermissions(new String[] { POST_NOTIFICATIONS }, 0);
+        }
+
         WindowManager.LayoutParams p = getWindow().getAttributes();
         p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
                 .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
index 1ab8ddb..27eb5a0 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -198,7 +198,7 @@
         filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
         filter.addAction(ACTION_ENTER_PIP);
         filter.addAction(ACTION_ASPECT_RATIO);
-        registerReceiver(mBroadcastReceiver, filter);
+        registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
 
         handleIntentExtra(getIntent());
     }
@@ -222,8 +222,8 @@
 
     private RemoteAction buildRemoteAction(Icon icon, String label, String action) {
         final Intent intent = new Intent(action);
-        final PendingIntent pendingIntent =
-                PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+        final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent,
+                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
         return new RemoteAction(icon, label, label, pendingIntent);
     }
 
diff --git a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
index 3a2a3be..ae32bda 100644
--- a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
+++ b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
@@ -16,6 +16,8 @@
 
 package android.hardware.input
 
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.content.ContextWrapper
 import android.graphics.drawable.Drawable
 import android.platform.test.annotations.Presubmit
@@ -54,16 +56,16 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
     fun testKeyboardLayoutDrawable_hasCorrectDimensions() {
-        setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
         val drawable = createDrawable()!!
         assertEquals(WIDTH, drawable.intrinsicWidth)
         assertEquals(HEIGHT, drawable.intrinsicHeight)
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
     fun testKeyboardLayoutDrawable_isNull_ifFlagOff() {
-        setFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
         assertNull(createDrawable())
     }
 }
\ No newline at end of file
diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
index e2b0c36..bcd56ad 100644
--- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
@@ -21,6 +21,7 @@
 import android.os.Handler
 import android.os.HandlerExecutor
 import android.os.test.TestLooper
+import android.platform.test.annotations.EnableFlags
 import android.platform.test.annotations.Presubmit
 import android.platform.test.flag.junit.SetFlagsRule
 import android.view.KeyEvent
@@ -50,6 +51,10 @@
  */
 @Presubmit
 @RunWith(MockitoJUnitRunner::class)
+@EnableFlags(
+    com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
+    com.android.input.flags.Flags.FLAG_ENABLE_INPUT_FILTER_RUST_IMPL,
+)
 class StickyModifierStateListenerTest {
 
     @get:Rule
@@ -67,10 +72,6 @@
 
     @Before
     fun setUp() {
-        // Enable Sticky keys feature
-        rule.enableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG)
-        rule.enableFlags(com.android.input.flags.Flags.FLAG_ENABLE_INPUT_FILTER_RUST_IMPL)
-
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
         inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
         inputManager = InputManager(context)
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java b/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java
similarity index 99%
rename from tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
rename to tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java
index 001a09a..be9fb1b 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java
@@ -34,7 +34,7 @@
 import perfetto.protos.ProtologCommon;
 import perfetto.protos.ProtologConfig;
 
-public class PerfettoDataSourceTest {
+public class ProtologDataSourceTest {
     @Before
     public void before() {
         assumeTrue(android.tracing.Flags.perfettoProtologTracing());
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 3ab8d37..6bcfebc 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -176,25 +176,19 @@
         },
         new Test("Disable Alerts") {
             public void run() {
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                info.setNotificationPeekingDisabled(true);
-                mStatusBarManager.requestDisabledComponent(info, "test");
+                mStatusBarManager.disable(StatusBarManager.DISABLE_NOTIFICATION_ALERTS);
             }
         },
         new Test("Disable Ticker") {
             public void run() {
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                info.setNotificationTickerDisabled(true);
-                mStatusBarManager.requestDisabledComponent(info, "test");
+                mStatusBarManager.disable(StatusBarManager.DISABLE_NOTIFICATION_TICKER);
             }
         },
         new Test("Disable Expand in 3 sec.") {
             public void run() {
                 mHandler.postDelayed(new Runnable() {
                         public void run() {
-                            StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                            info.setStatusBarExpansionDisabled(true);
-                            mStatusBarManager.requestDisabledComponent(info, "test");
+                            mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND);
                         }
                     }, 3000);
             }
@@ -203,9 +197,7 @@
             public void run() {
                 mHandler.postDelayed(new Runnable() {
                         public void run() {
-                            StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                            info.setNotificationIconsDisabled(true);
-                            mStatusBarManager.requestDisabledComponent(info, "test");
+                            mStatusBarManager.disable(StatusBarManager.DISABLE_NOTIFICATION_ICONS);
                         }
                     }, 3000);
             }
@@ -214,73 +206,56 @@
             public void run() {
                 mHandler.postDelayed(new Runnable() {
                         public void run() {
-                            StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                            info.setStatusBarExpansionDisabled(true);
-                            info.setNotificationIconsDisabled(true);
-                            mStatusBarManager.requestDisabledComponent(info, "test");
+                            mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND
+                                    | StatusBarManager.DISABLE_NOTIFICATION_ICONS);
                         }
                     }, 3000);
             }
         },
         new Test("Disable Home (StatusBarManager)") {
             public void run() {
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                info.setNavigationHomeDisabled(true);
-                mStatusBarManager.requestDisabledComponent(info, "test");
+                mStatusBarManager.disable(StatusBarManager.DISABLE_HOME);
             }
         },
         new Test("Disable Back (StatusBarManager)") {
             public void run() {
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                info.setBackDisabled(true);
-                mStatusBarManager.requestDisabledComponent(info, "test");
+                mStatusBarManager.disable(StatusBarManager.DISABLE_BACK);
             }
         },
         new Test("Disable Recent (StatusBarManager)") {
             public void run() {
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                info.setRecentsDisabled(true);
-                mStatusBarManager.requestDisabledComponent(info, "test");
+                mStatusBarManager.disable(StatusBarManager.DISABLE_RECENT);
             }
         },
         new Test("Disable Clock") {
             public void run() {
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                info.setClockDisabled(true);
-                mStatusBarManager.requestDisabledComponent(info, "test");
+                mStatusBarManager.disable(StatusBarManager.DISABLE_CLOCK);
             }
         },
         new Test("Disable System Info") {
             public void run() {
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                info.setSystemIconsDisabled(true);
-                mStatusBarManager.requestDisabledComponent(info, "test");
+                mStatusBarManager.disable(StatusBarManager.DISABLE_SYSTEM_INFO);
             }
         },
         new Test("Disable everything in 3 sec") {
             public void run() {
                 mHandler.postDelayed(new Runnable() {
                         public void run() {
-                            StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                            info.setDisableAll();
-                            mStatusBarManager.requestDisabledComponent(info, "test");
+                            mStatusBarManager.disable(~StatusBarManager.DISABLE_NONE);
                         }
                     }, 3000);
             }
         },
         new Test("Enable everything") {
             public void run() {
-                StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                mStatusBarManager.requestDisabledComponent(info, "test");
+                mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
             }
         },
         new Test("Enable everything in 3 sec.") {
             public void run() {
                 mHandler.postDelayed(new Runnable() {
                         public void run() {
-                            StatusBarManager.DisableInfo info = new StatusBarManager.DisableInfo();
-                            info.setEnableAll();
-                            mStatusBarManager.requestDisabledComponent(info, "test");
+                            mStatusBarManager.disable(0);
                         }
                     }, 3000);
             }
diff --git a/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java b/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java
new file mode 100644
index 0000000..d6f3148
--- /dev/null
+++ b/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.usb.flags.Flags;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.util.XmlUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.StringReader;
+
+/**
+ * Unit tests for {@link android.hardware.usb.DeviceFilter}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class DeviceFilterTest {
+
+    private static final int VID = 10;
+    private static final int PID = 11;
+    private static final int CLASS = 12;
+    private static final int SUBCLASS = 13;
+    private static final int PROTOCOL = 14;
+    private static final String MANUFACTURER = "Google";
+    private static final String PRODUCT = "Test";
+    private static final String SERIAL_NO = "4AL23";
+    private static final String INTERFACE_NAME = "MTP";
+
+    private MockitoSession mStaticMockSession;
+
+    @Before
+    public void setUp() throws Exception {
+        mStaticMockSession = ExtendedMockito.mockitoSession()
+                .mockStatic(Flags.class)
+                .strictness(Strictness.WARN)
+                .startMocking();
+
+        when(Flags.enableInterfaceNameDeviceFilter()).thenReturn(true);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mStaticMockSession.finishMocking();
+    }
+
+    @Test
+    public void testConstructorFromValues_interfaceNameIsInitialized() {
+        DeviceFilter deviceFilter = new DeviceFilter(
+                VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER,
+                PRODUCT, SERIAL_NO, INTERFACE_NAME
+        );
+
+        verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter);
+        assertThat(deviceFilter.mInterfaceName).isEqualTo(INTERFACE_NAME);
+    }
+
+    @Test
+    public void testConstructorFromUsbDevice_interfaceNameIsNull() {
+        UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+        when(usbDevice.getVendorId()).thenReturn(VID);
+        when(usbDevice.getProductId()).thenReturn(PID);
+        when(usbDevice.getDeviceClass()).thenReturn(CLASS);
+        when(usbDevice.getDeviceSubclass()).thenReturn(SUBCLASS);
+        when(usbDevice.getDeviceProtocol()).thenReturn(PROTOCOL);
+        when(usbDevice.getManufacturerName()).thenReturn(MANUFACTURER);
+        when(usbDevice.getProductName()).thenReturn(PRODUCT);
+        when(usbDevice.getSerialNumber()).thenReturn(SERIAL_NO);
+
+        DeviceFilter deviceFilter = new DeviceFilter(usbDevice);
+
+        verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter);
+        assertThat(deviceFilter.mInterfaceName).isEqualTo(null);
+    }
+
+    @Test
+    public void testConstructorFromDeviceFilter_interfaceNameIsInitialized() {
+        DeviceFilter originalDeviceFilter = new DeviceFilter(
+                VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER,
+                PRODUCT, SERIAL_NO, INTERFACE_NAME
+        );
+
+        DeviceFilter deviceFilter = new DeviceFilter(originalDeviceFilter);
+
+        verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter);
+        assertThat(deviceFilter.mInterfaceName).isEqualTo(INTERFACE_NAME);
+    }
+
+
+    @Test
+    public void testReadFromXml_interfaceNamePresent_propertyIsInitialized() throws Exception {
+        DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device interface-name=\"MTP\"/>");
+
+        assertThat(deviceFilter.mInterfaceName).isEqualTo("MTP");
+    }
+
+    @Test
+    public void testReadFromXml_interfaceNameAbsent_propertyIsNull() throws Exception {
+        DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device vendor-id=\"1\" />");
+
+        assertThat(deviceFilter.mInterfaceName).isEqualTo(null);
+    }
+
+    @Test
+    public void testWrite_withInterfaceName() throws Exception {
+        DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device interface-name=\"MTP\"/>");
+        XmlSerializer serializer = Mockito.mock(XmlSerializer.class);
+
+        deviceFilter.write(serializer);
+
+        verify(serializer).attribute(null, "interface-name", "MTP");
+    }
+
+    @Test
+    public void testWrite_withoutInterfaceName() throws Exception {
+        DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device vendor-id=\"1\" />");
+        XmlSerializer serializer = Mockito.mock(XmlSerializer.class);
+
+        deviceFilter.write(serializer);
+
+        verify(serializer, times(0)).attribute(eq(null), eq("interface-name"), any());
+    }
+
+    @Test
+    public void testToString() {
+        DeviceFilter deviceFilter = new DeviceFilter(
+                VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER,
+                PRODUCT, SERIAL_NO, INTERFACE_NAME
+        );
+
+        assertThat(deviceFilter.toString()).isEqualTo(
+                "DeviceFilter[mVendorId=10,mProductId=11,mClass=12,mSubclass=13,mProtocol=14,"
+                + "mManufacturerName=Google,mProductName=Test,mSerialNumber=4AL23,"
+                + "mInterfaceName=MTP]");
+    }
+
+    @Test
+    public void testMatch_interfaceNameMatches_returnTrue() throws Exception {
+        DeviceFilter deviceFilter = getDeviceFilterFromXml(
+                "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" "
+                + "interface-name=\"MTP\"/>");
+        UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+        when(usbDevice.getInterfaceCount()).thenReturn(1);
+        when(usbDevice.getInterface(0)).thenReturn(new UsbInterface(
+            /* id= */ 0,
+            /* alternateSetting= */ 0,
+            /* name= */ "MTP",
+            /* class= */ 255,
+            /* subClass= */ 255,
+            /* protocol= */ 0));
+
+        assertTrue(deviceFilter.matches(usbDevice));
+    }
+
+    @Test
+    public void testMatch_interfaceNameMismatch_returnFalse() throws Exception {
+        DeviceFilter deviceFilter = getDeviceFilterFromXml(
+                "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" "
+                + "interface-name=\"MTP\"/>");
+        UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+        when(usbDevice.getInterfaceCount()).thenReturn(1);
+        when(usbDevice.getInterface(0)).thenReturn(new UsbInterface(
+            /* id= */ 0,
+            /* alternateSetting= */ 0,
+            /* name= */ "UVC",
+            /* class= */ 255,
+            /* subClass= */ 255,
+            /* protocol= */ 0));
+
+        assertFalse(deviceFilter.matches(usbDevice));
+    }
+
+    @Test
+    public void testMatch_interfaceNameMismatchFlagDisabled_returnTrue() throws Exception {
+        when(Flags.enableInterfaceNameDeviceFilter()).thenReturn(false);
+        DeviceFilter deviceFilter = getDeviceFilterFromXml(
+                "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" "
+                + "interface-name=\"MTP\"/>");
+        UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+        when(usbDevice.getInterfaceCount()).thenReturn(1);
+        when(usbDevice.getInterface(0)).thenReturn(new UsbInterface(
+            /* id= */ 0,
+            /* alternateSetting= */ 0,
+            /* name= */ "UVC",
+            /* class= */ 255,
+            /* subClass= */ 255,
+            /* protocol= */ 0));
+
+        assertTrue(deviceFilter.matches(usbDevice));
+    }
+
+    private void verifyDeviceFilterConfigurationExceptInterfaceName(DeviceFilter deviceFilter) {
+        assertThat(deviceFilter.mVendorId).isEqualTo(VID);
+        assertThat(deviceFilter.mProductId).isEqualTo(PID);
+        assertThat(deviceFilter.mClass).isEqualTo(CLASS);
+        assertThat(deviceFilter.mSubclass).isEqualTo(SUBCLASS);
+        assertThat(deviceFilter.mProtocol).isEqualTo(PROTOCOL);
+        assertThat(deviceFilter.mManufacturerName).isEqualTo(MANUFACTURER);
+        assertThat(deviceFilter.mProductName).isEqualTo(PRODUCT);
+        assertThat(deviceFilter.mSerialNumber).isEqualTo(SERIAL_NO);
+    }
+
+    private DeviceFilter getDeviceFilterFromXml(String xml) throws Exception {
+        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+        XmlPullParser parser = factory.newPullParser();
+        parser.setInput(new StringReader(xml));
+        XmlUtils.nextElement(parser);
+
+        return DeviceFilter.read(parser);
+    }
+
+}
diff --git a/tests/graphics/SilkFX/AndroidManifest.xml b/tests/graphics/SilkFX/AndroidManifest.xml
index c293589..25092b5 100644
--- a/tests/graphics/SilkFX/AndroidManifest.xml
+++ b/tests/graphics/SilkFX/AndroidManifest.xml
@@ -23,12 +23,13 @@
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
     <application android:label="SilkFX"
-         android:theme="@android:style/Theme.Material">
+         android:theme="@style/Theme.UsefulDefault">
 
         <activity android:name=".Main"
              android:label="SilkFX Demos"
              android:banner="@drawable/background1"
-             android:exported="true">
+             android:exported="true"
+             android:theme="@style/Theme.UsefulDefault">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.DEFAULT"/>
diff --git a/tests/graphics/SilkFX/res/values/style.xml b/tests/graphics/SilkFX/res/values/style.xml
index 66edbb5..4dd626d 100644
--- a/tests/graphics/SilkFX/res/values/style.xml
+++ b/tests/graphics/SilkFX/res/values/style.xml
@@ -23,9 +23,14 @@
         <item name="android:windowElevation">0dp</item>
         <item name="buttonStyle">@style/AppTheme.Button</item>
         <item name="colorAccent">#bbffffff</item>
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
     </style>
     <style name="AppTheme.Button" parent="Widget.AppCompat.Button">
         <item name="android:textColor">#ffffffff</item>
     </style>
 
+    <style name="Theme.UsefulDefault" parent="android:Theme.Material">
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+    </style>
+
 </resources>
diff --git a/tools/aapt/Symbol.h b/tools/aapt/Symbol.h
index de1d60c..24c3208 100644
--- a/tools/aapt/Symbol.h
+++ b/tools/aapt/Symbol.h
@@ -40,7 +40,7 @@
 };
 
 /**
- * A specific defintion of a symbol, defined with a configuration and a definition site.
+ * A specific definition of a symbol, defined with a configuration and a definition site.
  */
 struct SymbolDefinition {
     inline SymbolDefinition();
@@ -92,4 +92,3 @@
 }
 
 #endif // AAPT_SYMBOL_H
-
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
index 16785d1..6b360b7 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
@@ -25,6 +25,7 @@
         val aidlPolicy: FilterPolicyWithReason?,
         val featureFlagsPolicy: FilterPolicyWithReason?,
         val syspropsPolicy: FilterPolicyWithReason?,
+        val rFilePolicy: FilterPolicyWithReason?,
         fallback: OutputFilter
 ) : DelegatingFilter(fallback) {
     override fun getPolicyForClass(className: String): FilterPolicyWithReason {
@@ -37,6 +38,9 @@
         if (syspropsPolicy != null && classes.isSyspropsClass(className)) {
             return syspropsPolicy
         }
+        if (rFilePolicy != null && classes.isRClass(className)) {
+            return rFilePolicy
+        }
         return super.getPolicyForClass(className)
     }
 }
@@ -74,3 +78,10 @@
     return className.startsWith("android/sysprop/")
             && className.endsWith("Properties")
 }
+
+/**
+ * @return if a given class "seems like" an R class or its nested classes.
+ */
+private fun ClassNodes.isRClass(className: String): Boolean {
+    return className.endsWith("/R") || className.contains("/R$")
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index 75b5fc8..c5acd81 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -17,6 +17,7 @@
 
 import com.android.hoststubgen.ParseException
 import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.toHumanReadableClassName
 import com.android.hoststubgen.log
 import com.android.hoststubgen.normalizeTextLine
 import com.android.hoststubgen.whitespaceRegex
@@ -31,13 +32,17 @@
  * Print a class node as a "keep" policy.
  */
 fun printAsTextPolicy(pw: PrintWriter, cn: ClassNode) {
-    pw.printf("class %s\t%s\n", cn.name, "keep")
+    pw.printf("class %s %s\n", cn.name.toHumanReadableClassName(), "keep")
 
-    for (f in cn.fields ?: emptyList()) {
-        pw.printf("  field %s\t%s\n", f.name, "keep")
+    cn.fields?.let {
+        for (f in it.sortedWith(compareBy({ it.name }))) {
+            pw.printf("    field %s %s\n", f.name, "keep")
+        }
     }
-    for (m in cn.methods ?: emptyList()) {
-        pw.printf("  method %s\t%s\t%s\n", m.name, m.desc, "keep")
+    cn.methods?.let {
+        for (m in it.sortedWith(compareBy({ it.name }, { it.desc }))) {
+            pw.printf("    method %s %s %s\n", m.name, m.desc, "keep")
+        }
     }
 }
 
@@ -66,6 +71,7 @@
         var aidlPolicy: FilterPolicyWithReason? = null
         var featureFlagsPolicy: FilterPolicyWithReason? = null
         var syspropsPolicy: FilterPolicyWithReason? = null
+        var rFilePolicy: FilterPolicyWithReason? = null
 
         try {
             BufferedReader(FileReader(filename)).use { reader ->
@@ -162,6 +168,14 @@
                                         syspropsPolicy = policy.withReason(
                                                 "$FILTER_REASON (special-class sysprops)")
                                     }
+                                    SpecialClass.RFile -> {
+                                        if (rFilePolicy != null) {
+                                            throw ParseException(
+                                                "Policy for R file already defined")
+                                        }
+                                        rFilePolicy = policy.withReason(
+                                            "$FILTER_REASON (special-class R file)")
+                                    }
                                 }
                             }
                         }
@@ -225,13 +239,9 @@
             throw e.withSourceInfo(filename, lineNo)
         }
 
-        var ret: OutputFilter = imf
-        if (aidlPolicy != null || featureFlagsPolicy != null || syspropsPolicy != null) {
-            log.d("AndroidHeuristicsFilter enabled")
-            ret = AndroidHeuristicsFilter(
-                    classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, imf)
-        }
-        return ret
+        // Wrap the in-memory-filter with AHF.
+        return AndroidHeuristicsFilter(
+                classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, rFilePolicy, imf)
     }
 }
 
@@ -240,6 +250,7 @@
     Aidl,
     FeatureFlags,
     Sysprops,
+    RFile,
 }
 
 private fun resolveSpecialClass(className: String): SpecialClass {
@@ -250,6 +261,7 @@
         ":aidl" -> return SpecialClass.Aidl
         ":feature_flags" -> return SpecialClass.FeatureFlags
         ":sysprops" -> return SpecialClass.Sysprops
+        ":r" -> return SpecialClass.RFile
     }
     throw ParseException("Invalid special class name \"$className\"")
 }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index fa8fe6c..931f0c5 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -322,6 +322,78 @@
 InnerClasses:
   public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
   public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 3
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: iconst_1
+         x: newarray       int
+         x: dup
+         x: iconst_0
+         x: iconst_1
+         x: iastore
+         x: putstatic     #x                  // Field ARRAY:[I
+        x: return
+      LineNumberTable:
+}
+SourceFile: "R.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+InnerClasses:
+  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 3
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R;
+}
+SourceFile: "R.java"
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
+InnerClasses:
+  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index c605f76..906a81c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -122,6 +122,100 @@
 NestMembers:
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 4
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 11d5939..10bc91d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -348,6 +348,108 @@
 NestMembers:
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: iconst_1
+         x: newarray       int
+         x: dup
+         x: iconst_0
+         x: iconst_1
+         x: iastore
+         x: putstatic     #x                 // Field ARRAY:[I
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 4
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index c605f76..906a81c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -122,6 +122,100 @@
 NestMembers:
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 4
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 088bc80..fcf9a8c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -481,6 +481,136 @@
 NestMembers:
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
+        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: iconst_1
+        x: newarray       int
+        x: dup
+        x: iconst_0
+        x: iconst_1
+        x: iastore
+        x: putstatic     #x                 // Field ARRAY:[I
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 2, attributes: 4
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index d302084..696b6d0 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -19,6 +19,9 @@
 # Heuristics rule: Stub all the AIDL classes.
 class :aidl stubclass
 
+# Heuristics rule: Stub all the R classes.
+class :r stubclass
+
 # Default is "remove", so let's put all the base classes / interfaces in the stub first.
 class com.android.hoststubgen.test.tinyframework.subclasstest.C1 stub
 class com.android.hoststubgen.test.tinyframework.subclasstest.C2 stub
diff --git a/core/java/android/app/StatusBarManager.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
similarity index 64%
rename from core/java/android/app/StatusBarManager.aidl
rename to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
index 687678c..b1bedf4 100644
--- a/core/java/android/app/StatusBarManager.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2024, The Android Open Source Project
+/*
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -13,7 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework;
 
-package android.app;
-
-parcelable StatusBarManager.DisableInfo;
\ No newline at end of file
+public class R {
+    public static class Nested {
+        public static int[] ARRAY = new int[] {1};
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 762180d..37925e8 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -19,6 +19,7 @@
 
 import static org.junit.Assert.fail;
 
+import com.android.hoststubgen.test.tinyframework.R.Nested;
 import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass;
 
 import org.junit.Rule;
@@ -328,4 +329,9 @@
         assertThat(IPretendingAidl.Stub.addOne(1)).isEqualTo(2);
         assertThat(IPretendingAidl.Stub.Proxy.addTwo(1)).isEqualTo(3);
     }
+
+    @Test
+    public void testRFileHeuristics() {
+        assertThat(Nested.ARRAY.length).isEqualTo(1);
+    }
 }